mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-26 13:47:03 +02:00
Fix some page i18n wrong (#17682)
* fix: some pages can't translation object label. * revert: revert wrong label fix * feat: add openai base_url setting * fix: fix classification modelSize i18n error * revert: revert openai base_url setting * fix: fix enrichments pages i18n keys wrong * fix: fix mobile bottom bar reindexing embeddings i18n wrong * feat: add more system stats i18n keys * fix: fix review filter objects i18n * chore: remove frigate+ label i18n
This commit is contained in:
parent
89b54f19c8
commit
c8e22a3653
@ -54,7 +54,12 @@
|
||||
},
|
||||
"gone": "{{label}} left",
|
||||
"heard": "{{label}} heard",
|
||||
"external": "{{label}} detected"
|
||||
"external": "{{label}} detected",
|
||||
"header": {
|
||||
"zones": "Zones",
|
||||
"ratio": "Ratio",
|
||||
"area": "Area"
|
||||
}
|
||||
},
|
||||
"annotationSettings": {
|
||||
"title": "Annotation Settings",
|
||||
|
@ -142,7 +142,10 @@
|
||||
"ffmpegHighCpuUsage": "{{camera}} has high FFMPEG CPU usage ({{ffmpegAvg}}%)",
|
||||
"detectHighCpuUsage": "{{camera}} has high detect CPU usage ({{detectAvg}}%)",
|
||||
"healthy": "System is healthy",
|
||||
"reindexingEmbeddings": "Reindexing embeddings ({{processed}}% complete)"
|
||||
"reindexingEmbeddings": "Reindexing embeddings ({{processed}}% complete)",
|
||||
"cameraIsOffline": "{{camera}} is offline",
|
||||
"detectIsSlow": "{{detect}} is slow ({{speed}} ms)",
|
||||
"detectIsVerySlow": "{{detect}} is very slow ({{speed}} ms)"
|
||||
},
|
||||
"enrichments": {
|
||||
"title": "Enrichments",
|
||||
@ -150,6 +153,7 @@
|
||||
"embeddings": {
|
||||
"image_embedding_speed": "Image Embedding Speed",
|
||||
"face_embedding_speed": "Face Embedding Speed",
|
||||
"face_recognition_speed": "Face Recognition Speed",
|
||||
"plate_recognition_speed": "Plate Recognition Speed",
|
||||
"text_embedding_speed": "Text Embedding Speed"
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ export function GeneralFilterContent({
|
||||
{allLabels.map((item) => (
|
||||
<FilterSwitch
|
||||
key={item}
|
||||
label={item.replaceAll("_", " ")}
|
||||
label={t(item, { ns: "objects" })}
|
||||
isChecked={filter.labels?.includes(item) ?? false}
|
||||
onCheckedChange={(isChecked) => {
|
||||
if (isChecked) {
|
||||
|
@ -263,7 +263,9 @@ function GeneralFilterButton({
|
||||
}
|
||||
|
||||
if (selectedLabels.length == 1) {
|
||||
return t(selectedLabels[0], { ns: "objects" });
|
||||
return t(selectedLabels[0], {
|
||||
ns: "objects",
|
||||
});
|
||||
}
|
||||
|
||||
return t("labels.count", {
|
||||
|
@ -419,7 +419,9 @@ export default function InputWithTags({
|
||||
? t("button.yes", { ns: "common" })
|
||||
: t("button.no", { ns: "common" });
|
||||
} else if (filterType === "labels") {
|
||||
return t(filterValues as string, { ns: "objects" });
|
||||
return t(filterValues as string, {
|
||||
ns: "objects",
|
||||
});
|
||||
} else if (filterType === "search_type") {
|
||||
return t("filter.searchType." + (filterValues as string));
|
||||
} else {
|
||||
@ -817,7 +819,11 @@ export default function InputWithTags({
|
||||
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800"
|
||||
>
|
||||
{t("filter.label." + filterType)}:{" "}
|
||||
{value.replaceAll("_", " ")}
|
||||
{filterType === "labels"
|
||||
? t(value, {
|
||||
ns: "objects",
|
||||
})
|
||||
: value.replaceAll("_", " ")}
|
||||
<button
|
||||
onClick={() =>
|
||||
removeFilter(filterType as FilterType, value)
|
||||
|
@ -16,6 +16,7 @@ import { Link } from "react-router-dom";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { isIOS, isMobile } from "react-device-detect";
|
||||
import { isPWA } from "@/utils/isPWA";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function Bottombar() {
|
||||
const navItems = useNavigation("secondary");
|
||||
@ -43,6 +44,7 @@ type StatusAlertNavProps = {
|
||||
className?: string;
|
||||
};
|
||||
function StatusAlertNav({ className }: StatusAlertNavProps) {
|
||||
const { t } = useTranslation(["views/system"]);
|
||||
const { data: initialStats } = useSWR<FrigateStats>("stats", {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
@ -82,14 +84,19 @@ function StatusAlertNav({ className }: StatusAlertNavProps) {
|
||||
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]);
|
||||
|
||||
if (!messages || Object.keys(messages).length === 0) {
|
||||
return;
|
||||
|
@ -596,7 +596,9 @@ export default function ObjectLifecycle({
|
||||
<div className="text-md mr-2 w-1/3">
|
||||
<div className="flex flex-col items-end justify-start">
|
||||
<p className="mb-1.5 text-sm text-primary-variant">
|
||||
Zones
|
||||
{t(
|
||||
"objectLifecycle.lifecycleItemDesc.header.zones",
|
||||
)}
|
||||
</p>
|
||||
{item.class_type === "entered_zone"
|
||||
? item.data.zones.map((zone, index) => (
|
||||
@ -627,7 +629,9 @@ export default function ObjectLifecycle({
|
||||
<div className="text-md mr-2 w-1/3">
|
||||
<div className="flex flex-col items-end justify-start">
|
||||
<p className="mb-1.5 text-sm text-primary-variant">
|
||||
Ratio
|
||||
{t(
|
||||
"objectLifecycle.lifecycleItemDesc.header.ratio",
|
||||
)}
|
||||
</p>
|
||||
{Array.isArray(item.data.box) &&
|
||||
item.data.box.length >= 4
|
||||
@ -641,7 +645,7 @@ export default function ObjectLifecycle({
|
||||
<div className="text-md mr-2 w-1/3">
|
||||
<div className="flex flex-col items-end justify-start">
|
||||
<p className="mb-1.5 text-sm text-primary-variant">
|
||||
Area
|
||||
{t("objectLifecycle.lifecycleItemDesc.header.area")}
|
||||
</p>
|
||||
{Array.isArray(item.data.box) &&
|
||||
item.data.box.length >= 4 ? (
|
||||
|
@ -320,7 +320,11 @@ export default function ReviewDetailDialog({
|
||||
ns="views/explore"
|
||||
values={{
|
||||
objects: missingObjects
|
||||
.map((x) => t(x, { ns: "objects" }))
|
||||
.map((x) =>
|
||||
t(x, {
|
||||
ns: "objects",
|
||||
}),
|
||||
)
|
||||
.join(", "),
|
||||
}}
|
||||
>
|
||||
|
@ -709,7 +709,9 @@ function ObjectDetailsTab({
|
||||
<div className="text-sm text-primary/40">{t("details.label")}</div>
|
||||
<div className="flex flex-row items-center gap-2 text-sm capitalize">
|
||||
{getIconForLabel(search.label, "size-4 text-primary")}
|
||||
{t(search.label, { ns: "objects" })}
|
||||
{t(search.label, {
|
||||
ns: "objects",
|
||||
})}
|
||||
{search.sub_label && ` (${search.sub_label})`}
|
||||
{isAdmin && (
|
||||
<Tooltip>
|
||||
@ -980,7 +982,7 @@ function ObjectDetailsTab({
|
||||
description={
|
||||
search.label
|
||||
? t("details.editSubLabel.desc", {
|
||||
label: t(search.label, { ns: "objects" }),
|
||||
label: search.label,
|
||||
})
|
||||
: t("details.editSubLabel.descNoLabel")
|
||||
}
|
||||
@ -995,7 +997,7 @@ function ObjectDetailsTab({
|
||||
description={
|
||||
search.label
|
||||
? t("details.editLPR.desc", {
|
||||
label: t(search.label, { ns: "objects" }),
|
||||
label: search.label,
|
||||
})
|
||||
: t("details.editLPR.descNoLabel")
|
||||
}
|
||||
|
@ -36,13 +36,19 @@ export default function useStats(stats: FrigateStats | undefined) {
|
||||
Object.entries(memoizedStats["detectors"]).forEach(([key, det]) => {
|
||||
if (det["inference_speed"] > InferenceThreshold.error) {
|
||||
problems.push({
|
||||
text: `${capitalizeFirstLetter(key)} is very slow (${det["inference_speed"]} ms)`,
|
||||
text: t("stats.detectIsVerySlow", {
|
||||
detect: capitalizeFirstLetter(key),
|
||||
speed: det["inference_speed"],
|
||||
}),
|
||||
color: "text-danger",
|
||||
relevantLink: "/system#general",
|
||||
});
|
||||
} else if (det["inference_speed"] > InferenceThreshold.warning) {
|
||||
problems.push({
|
||||
text: `${capitalizeFirstLetter(key)} is slow (${det["inference_speed"]} ms)`,
|
||||
text: t("stats.detectIsSlow", {
|
||||
detect: capitalizeFirstLetter(key),
|
||||
speed: det["inference_speed"],
|
||||
}),
|
||||
color: "text-orange-400",
|
||||
relevantLink: "/system#general",
|
||||
});
|
||||
@ -57,7 +63,9 @@ export default function useStats(stats: FrigateStats | undefined) {
|
||||
|
||||
if (config.cameras[name].enabled && cam["camera_fps"] == 0) {
|
||||
problems.push({
|
||||
text: `${capitalizeFirstLetter(name.replaceAll("_", " "))} is offline`,
|
||||
text: t("stats.cameraIsOffline", {
|
||||
camera: capitalizeFirstLetter(name.replaceAll("_", " ")),
|
||||
}),
|
||||
color: "text-danger",
|
||||
relevantLink: "logs",
|
||||
});
|
||||
|
@ -5,11 +5,16 @@ export function getLifecycleItemDescription(
|
||||
lifecycleItem: ObjectLifecycleSequence,
|
||||
) {
|
||||
// can't use useTranslation here
|
||||
const label = (
|
||||
(Array.isArray(lifecycleItem.data.sub_label)
|
||||
? lifecycleItem.data.sub_label[0]
|
||||
: lifecycleItem.data.sub_label) || lifecycleItem.data.label
|
||||
).replaceAll("_", " ");
|
||||
const label = t(
|
||||
(
|
||||
(Array.isArray(lifecycleItem.data.sub_label)
|
||||
? lifecycleItem.data.sub_label[0]
|
||||
: lifecycleItem.data.sub_label) || lifecycleItem.data.label
|
||||
)
|
||||
.replace(" ", "_")
|
||||
.toLowerCase(),
|
||||
{ ns: "objects" },
|
||||
);
|
||||
|
||||
switch (lifecycleItem.class_type) {
|
||||
case "visible":
|
||||
|
@ -336,7 +336,9 @@ export default function ClassificationSettingsView({
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-20">
|
||||
{classificationSettings.search.model_size}
|
||||
{t(
|
||||
`classification.semanticSearch.modelSize.${classificationSettings.search.model_size}.title`,
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
@ -466,7 +468,9 @@ export default function ClassificationSettingsView({
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-20">
|
||||
{classificationSettings.face.model_size}
|
||||
{t(
|
||||
`classification.faceRecognition.modelSize.${classificationSettings.face.model_size}.title`,
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
|
@ -366,7 +366,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
|
||||
{getIconForLabel(obj.label, "size-5 text-white")}
|
||||
</div>
|
||||
<div className="ml-3 text-lg">
|
||||
{capitalizeFirstLetter(obj.label.replaceAll("_", " "))}
|
||||
{t(obj.label, { ns: "objects" })}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-8/12 flex-row items-center justify-end">
|
||||
|
@ -77,7 +77,10 @@ export default function EnrichmentMetrics({
|
||||
const key = rawKey.replaceAll("_", " ");
|
||||
|
||||
if (!(key in series)) {
|
||||
series[key] = { name: t("features.embeddings." + rawKey), data: [] };
|
||||
series[key] = {
|
||||
name: t("enrichments.embeddings." + rawKey),
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
series[key].data.push({ x: statsIdx + 1, y: stat });
|
||||
@ -90,7 +93,7 @@ export default function EnrichmentMetrics({
|
||||
<>
|
||||
<div className="scrollbar-container mt-4 flex size-full flex-col overflow-y-auto">
|
||||
<div className="text-sm font-medium text-muted-foreground">
|
||||
{t("features.title")}
|
||||
{t("enrichments.title")}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
|
Loading…
Reference in New Issue
Block a user