feat: add i18n (translation/localization) (#16877)

* Translation module init

* Add more i18n keys

* fix: fix string wrong

* refactor: use namespace translation file

* chore: add more translation key

* fix: fix some page name error

* refactor: change Trans tag for t function

* chore: fix some key not work

* chore: fix SearchFilterDialog i18n key error

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* chore: fix en i18n file filter missing some keys

* chore: add some i18n keys

* chore: add more i18n keys again

* feat: add search page i18n

* feat: add explore model i18n keys

* Update web/src/components/menu/GeneralSettings.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/components/menu/GeneralSettings.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/components/menu/GeneralSettings.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* feat: add more live i18n keys

* feat: add more search setting i18n keys

* fix: remove some comment

* fix: fix some setting page url error

* Update web/src/views/settings/SearchSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* fix: add system missing keys

* fix: update password update i18n keys

* chore: remove outdate translation.json file

* fix: fix exploreSettings error

* chore: add object setting i18n keys

* Update web/src/views/recording/RecordingView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/public/locales/en/components/filter.json

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/components/overlay/ExportDialog.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* feat: add more i18n keys

* fix: fix motionDetectionTuner html node

* feat: add more page i18n keys

* fix: cameraStream i18n keys error

* feat: add Player i18n keys

* feat: add more toast i18n keys

* feat: change explore setting name

* feat: add more document title i18n keys

* feat: add more search i18n keys

* fix: fix accessDenied i18n keys error

* chore: add objectType i18n

* chore: add  inputWithTags i18n

* chore: add SearchFilterDialog i18n

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* chore: add some missing i18n keys

* chore: remove most import { t } from "i18next";

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
This commit is contained in:
GuoQing Liu
2025-03-16 23:36:20 +08:00
committed by GitHub
parent db541abed4
commit d34533981f
150 changed files with 6810 additions and 1927 deletions

View File

@@ -23,9 +23,11 @@ import { getIconForLabel } from "@/utils/iconUtil";
import { capitalizeFirstLetter } from "@/utils/stringUtil";
import { LuExternalLink, LuInfo } from "react-icons/lu";
import { Link } from "react-router-dom";
import DebugDrawingLayer from "@/components/overlay/DebugDrawingLayer";
import { Separator } from "@/components/ui/separator";
import { isDesktop } from "react-device-detect";
import { Trans, useTranslation } from "react-i18next";
type ObjectSettingsViewProps = {
selectedCamera?: string;
@@ -38,6 +40,8 @@ const emptyObject = Object.freeze({});
export default function ObjectSettingsView({
selectedCamera,
}: ObjectSettingsViewProps) {
const { t } = useTranslation(["views/settings"]);
const { data: config } = useSWR<FrigateConfig>("config");
const containerRef = useRef<HTMLDivElement>(null);
@@ -45,80 +49,45 @@ export default function ObjectSettingsView({
const DEBUG_OPTIONS = [
{
param: "bbox",
title: "Bounding boxes",
description: "Show bounding boxes around tracked objects",
title: t("debug.boundingBoxes.title"),
description: t("debug.boundingBoxes.desc"),
info: (
<>
<p className="mb-2">
<strong>Object Bounding Box Colors</strong>
<strong>{t("debug.boundingBoxes.colors.label")}</strong>
</p>
<ul className="list-disc space-y-1 pl-5">
<li>
At startup, different colors will be assigned to each object label
</li>
<li>
A dark blue thin line indicates that object is not detected at
this current point in time
</li>
<li>
A gray thin line indicates that object is detected as being
stationary
</li>
<li>
A thick line indicates that object is the subject of autotracking
(when enabled)
</li>
<Trans ns="views/settings">debug.boundingBoxes.colors.info</Trans>
</ul>
</>
),
},
{
param: "timestamp",
title: "Timestamp",
description: "Overlay a timestamp on the image",
title: t("debug.timestamp.title"),
description: t("debug.timestamp.desc"),
},
{
param: "zones",
title: "Zones",
description: "Show an outline of any defined zones",
title: t("debug.zones.title"),
description: t("debug.zones.desc"),
},
{
param: "mask",
title: "Motion masks",
description: "Show motion mask polygons",
title: t("debug.mask.title"),
description: t("debug.mask.desc"),
},
{
param: "motion",
title: "Motion boxes",
description: "Show boxes around areas where motion is detected",
info: (
<>
<p className="mb-2">
<strong>Motion Boxes</strong>
</p>
<p>
Red boxes will be overlaid on areas of the frame where motion is
currently being detected
</p>
</>
),
title: t("debug.motion.title"),
description: t("debug.motion.desc"),
info: <Trans ns="views/settings">debug.motion.tips</Trans>,
},
{
param: "regions",
title: "Regions",
description:
"Show a box of the region of interest sent to the object detector",
info: (
<>
<p className="mb-2">
<strong>Region Boxes</strong>
</p>
<p>
Bright green boxes will be overlaid on areas of interest in the
frame that are being sent to the object detector.
</p>
</>
),
title: t("debug.regions.title"),
description: t("debug.regions.desc"),
info: <Trans ns="views/settings">debug.regions.tips</Trans>,
},
];
@@ -167,8 +136,8 @@ export default function ObjectSettingsView({
}, [options, optionsLoaded]);
useEffect(() => {
document.title = "Object Settings - Frigate";
}, []);
document.title = t("documentTitle.object");
}, [t]);
if (!cameraConfig) {
return <ActivityIndicator />;
@@ -179,25 +148,19 @@ export default function ObjectSettingsView({
<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 md:w-3/12">
<Heading as="h3" className="my-2">
Debug
{t("debug.title")}
</Heading>
<div className="mb-5 space-y-3 text-sm text-muted-foreground">
<p>
Frigate uses your detectors{" "}
{config
? "(" +
Object.keys(config?.detectors)
.map((detector) => capitalizeFirstLetter(detector))
.join(",") +
")"
: ""}{" "}
to detect objects in your camera's video stream.
</p>
<p>
Debugging view shows a real-time view of tracked objects and their
statistics. The object list shows a time-delayed summary of detected
objects.
{t("debug.detectorDesc", {
detectors: config
? Object.keys(config?.detectors)
.map((detector) => capitalizeFirstLetter(detector))
.join(",")
: "",
})}
</p>
<p>{t("debug.desc")}</p>
</div>
{config?.cameras[cameraConfig.name]?.webui_url && (
<div className="mb-5 text-sm text-muted-foreground">
@@ -217,8 +180,10 @@ export default function ObjectSettingsView({
<Tabs defaultValue="debug" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="debug">Debugging</TabsTrigger>
<TabsTrigger value="objectlist">Object List</TabsTrigger>
<TabsTrigger value="debug">{t("debug.debugging")}</TabsTrigger>
<TabsTrigger value="objectlist">
{t("debug.objectList")}
</TabsTrigger>
</TabsList>
<TabsContent value="debug">
<div className="flex w-full flex-col space-y-6">
@@ -277,21 +242,20 @@ export default function ObjectSettingsView({
className="mb-0 cursor-pointer capitalize text-primary"
htmlFor="debugdraw"
>
Object Shape Filter Drawing
{t("debug.objectShapeFilterDrawing.title")}
</Label>
<Popover>
<PopoverTrigger asChild>
<div className="cursor-pointer p-0">
<LuInfo className="size-4" />
<span className="sr-only">Info</span>
<span className="sr-only">
{t("button.info", { ns: "common" })}
</span>
</div>
</PopoverTrigger>
<PopoverContent className="w-80 text-sm">
Enable this option to draw a rectangle on the
camera image to show its area and ratio. These
values can then be used to set object shape filter
parameters in your config.
{t("debug.objectShapeFilterDrawing.tips")}
<div className="mt-2 flex items-center text-primary">
<Link
to="https://docs.frigate.video/configuration/object_filters#object-shape"
@@ -299,7 +263,7 @@ export default function ObjectSettingsView({
rel="noopener noreferrer"
className="inline"
>
Read the documentation{" "}
{t("debug.objectShapeFilterDrawing.document")}
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
@@ -307,8 +271,7 @@ export default function ObjectSettingsView({
</Popover>
</div>
<div className="mt-1 text-xs text-muted-foreground">
Draw a rectangle on the image to view area and ratio
details
{t("debug.objectShapeFilterDrawing.desc")}
</div>
</div>
<Switch
@@ -364,6 +327,7 @@ type ObjectListProps = {
};
function ObjectList({ cameraConfig, objects }: ObjectListProps) {
const { t } = useTranslation(["views/settings"]);
const { data: config } = useSWR<FrigateConfig>("config");
const colormap = useMemo(() => {
@@ -409,7 +373,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
<div className="text-md mr-2 w-1/3">
<div className="flex flex-col items-end justify-end">
<p className="mb-1.5 text-sm text-primary-variant">
Score
{t("debug.objectShapeFilterDrawing.score")}
</p>
{obj.score
? (obj.score * 100).toFixed(1).toString()
@@ -420,7 +384,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
<div className="text-md mr-2 w-1/3">
<div className="flex flex-col items-end justify-end">
<p className="mb-1.5 text-sm text-primary-variant">
Ratio
{t("debug.objectShapeFilterDrawing.ratio")}
</p>
{obj.ratio ? obj.ratio.toFixed(2).toString() : "-"}
</div>
@@ -428,7 +392,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
<div className="text-md mr-2 w-1/3">
<div className="flex flex-col items-end justify-end">
<p className="mb-1.5 text-sm text-primary-variant">
Area
{t("debug.objectShapeFilterDrawing.area")}
</p>
{obj.area ? (
<>
@@ -457,7 +421,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
);
})
) : (
<div className="p-3 text-center">No objects</div>
<div className="p-3 text-center">{t("debug.noObjects")}</div>
)}
</div>
);