mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-29 15:19:00 +03:00
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:
co-authored by
Josh Hawkins
parent
db541abed4
commit
d34533981f
@@ -12,6 +12,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import useSWR from "swr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type CameraMetricsProps = {
|
||||
lastUpdated: number;
|
||||
@@ -22,7 +23,7 @@ export default function CameraMetrics({
|
||||
setLastUpdated,
|
||||
}: CameraMetricsProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const { t } = useTranslation(["views/system"]);
|
||||
// camera info dialog
|
||||
|
||||
const [showCameraInfoDialog, setShowCameraInfoDialog] = useState(false);
|
||||
@@ -223,11 +224,13 @@ export default function CameraMetrics({
|
||||
|
||||
return (
|
||||
<div className="scrollbar-container mt-4 flex size-full flex-col gap-3 overflow-y-auto">
|
||||
<div className="text-sm font-medium text-muted-foreground">Overview</div>
|
||||
<div className="text-sm font-medium text-muted-foreground">
|
||||
{t("cameras.overview")}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3">
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Frames / Detections</div>
|
||||
<div className="mb-5">{t("cameras.framesAndDetections")}</div>
|
||||
<CameraLineGraph
|
||||
graphId="overall-stats"
|
||||
unit=""
|
||||
@@ -269,7 +272,9 @@ export default function CameraMetrics({
|
||||
}}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Camera Probe Info</TooltipContent>
|
||||
<TooltipContent>
|
||||
{t("cameras.info.tips.title")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
@@ -294,7 +299,9 @@ export default function CameraMetrics({
|
||||
)}
|
||||
{Object.keys(cameraFpsSeries).includes(camera.name) ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Frames / Detections</div>
|
||||
<div className="mb-5">
|
||||
{t("cameras.framesAndDetections")}
|
||||
</div>
|
||||
<CameraLineGraph
|
||||
graphId={`${camera.name}-dps`}
|
||||
unit=""
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EmbeddingThreshold } from "@/types/graph";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ThresholdBarGraph } from "@/components/graph/SystemGraph";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type FeatureMetricsProps = {
|
||||
lastUpdated: number;
|
||||
@@ -16,6 +17,7 @@ export default function FeatureMetrics({
|
||||
setLastUpdated,
|
||||
}: FeatureMetricsProps) {
|
||||
// stats
|
||||
const { t } = useTranslation(["views/system"]);
|
||||
|
||||
const { data: initialStats } = useSWR<FrigateStats[]>(
|
||||
["stats/history", { keys: "embeddings,service" }],
|
||||
@@ -87,7 +89,7 @@ export default function FeatureMetrics({
|
||||
<>
|
||||
<div className="scrollbar-container mt-4 flex size-full flex-col overflow-y-auto">
|
||||
<div className="text-sm font-medium text-muted-foreground">
|
||||
Features
|
||||
{t("features.title")}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -15,6 +15,7 @@ import GPUInfoDialog from "@/components/overlay/GPUInfoDialog";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ThresholdBarGraph } from "@/components/graph/SystemGraph";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type GeneralMetricsProps = {
|
||||
lastUpdated: number;
|
||||
@@ -25,7 +26,7 @@ export default function GeneralMetrics({
|
||||
setLastUpdated,
|
||||
}: GeneralMetricsProps) {
|
||||
// extra info
|
||||
|
||||
const { t } = useTranslation(["views/system"]);
|
||||
const [showVainfo, setShowVainfo] = useState(false);
|
||||
|
||||
// stats
|
||||
@@ -448,7 +449,7 @@ export default function GeneralMetrics({
|
||||
|
||||
<div className="scrollbar-container mt-4 flex size-full flex-col overflow-y-auto">
|
||||
<div className="text-sm font-medium text-muted-foreground">
|
||||
Detectors
|
||||
{t("general.detector.title")}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
@@ -458,7 +459,7 @@ export default function GeneralMetrics({
|
||||
>
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Detector Inference Speed</div>
|
||||
<div className="mb-5">{t("general.detector.inferenceSpeed")}</div>
|
||||
{detInferenceTimeSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -496,7 +497,7 @@ export default function GeneralMetrics({
|
||||
)}
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Detector CPU Usage</div>
|
||||
<div className="mb-5">{t("general.detector.cpuUsage")}</div>
|
||||
{detCpuSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -514,7 +515,7 @@ export default function GeneralMetrics({
|
||||
)}
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Detector Memory Usage</div>
|
||||
<div className="mb-5">{t("general.detector.memoryUsage")}</div>
|
||||
{detMemSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -541,11 +542,11 @@ export default function GeneralMetrics({
|
||||
{canGetGpuInfo && (
|
||||
<Button
|
||||
className="cursor-pointer"
|
||||
aria-label="Hardware information"
|
||||
aria-label={t("general.hardwareInfo.title")}
|
||||
size="sm"
|
||||
onClick={() => setShowVainfo(true)}
|
||||
>
|
||||
Hardware Info
|
||||
{t("general.hardwareInfo.title")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -557,7 +558,9 @@ export default function GeneralMetrics({
|
||||
>
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">GPU Usage</div>
|
||||
<div className="mb-5">
|
||||
{t("general.hardwareInfo.gpuUsage")}
|
||||
</div>
|
||||
{gpuSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -577,7 +580,9 @@ export default function GeneralMetrics({
|
||||
<>
|
||||
{gpuMemSeries && (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">GPU Memory</div>
|
||||
<div className="mb-5">
|
||||
{t("general.hardwareInfo.gpuMemroy")}
|
||||
</div>
|
||||
{gpuMemSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -599,7 +604,9 @@ export default function GeneralMetrics({
|
||||
<>
|
||||
{gpuEncSeries && gpuEncSeries?.length != 0 && (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">GPU Encoder</div>
|
||||
<div className="mb-5">
|
||||
{t("general.hardwareInfo.gpuEncoder")}
|
||||
</div>
|
||||
{gpuEncSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -621,7 +628,9 @@ export default function GeneralMetrics({
|
||||
<>
|
||||
{gpuDecSeries && gpuDecSeries?.length != 0 && (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">GPU Decoder</div>
|
||||
<div className="mb-5">
|
||||
{t("general.hardwareInfo.gpuDecoder")}
|
||||
</div>
|
||||
{gpuDecSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -644,12 +653,14 @@ export default function GeneralMetrics({
|
||||
)}
|
||||
|
||||
<div className="mt-4 text-sm font-medium text-muted-foreground">
|
||||
Other Processes
|
||||
{t("general.otherProcesses.title")}
|
||||
</div>
|
||||
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Process CPU Usage</div>
|
||||
<div className="mb-5">
|
||||
{t("general.otherProcesses.processCpuUsage")}
|
||||
</div>
|
||||
{otherProcessCpuSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
@@ -667,7 +678,9 @@ export default function GeneralMetrics({
|
||||
)}
|
||||
{statsHistory.length != 0 ? (
|
||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5">Process Memory Usage</div>
|
||||
<div className="mb-5">
|
||||
{t("general.otherProcesses.processMemoryUsage")}
|
||||
</div>
|
||||
{otherProcessMemSeries.map((series) => (
|
||||
<ThresholdBarGraph
|
||||
key={series.name}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import { useTimezone } from "@/hooks/use-date-utils";
|
||||
import { RecordingsSummary } from "@/types/review";
|
||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type CameraStorage = {
|
||||
[key: string]: {
|
||||
@@ -33,7 +34,7 @@ export default function StorageMetrics({
|
||||
const { data: config } = useSWR<FrigateConfig>("config", {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
const { t } = useTranslation(["views/system"]);
|
||||
const timezone = useTimezone(config);
|
||||
|
||||
const totalStorage = useMemo(() => {
|
||||
@@ -75,29 +76,31 @@ export default function StorageMetrics({
|
||||
|
||||
return (
|
||||
<div className="scrollbar-container mt-4 flex size-full flex-col overflow-y-auto">
|
||||
<div className="text-sm font-medium text-muted-foreground">Overview</div>
|
||||
<div className="text-sm font-medium text-muted-foreground">
|
||||
{t("storage.overview")}
|
||||
</div>
|
||||
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
|
||||
<div className="flex-col rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<div className="mb-5 flex flex-row items-center justify-between">
|
||||
Recordings
|
||||
{t("storage.recordings.title")}
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
className="focus:outline-none"
|
||||
aria-label="Unused Storage Information"
|
||||
aria-label={t(
|
||||
"storage.cameraStorage.unusedStorageInformation",
|
||||
)}
|
||||
>
|
||||
<CiCircleAlert
|
||||
className="size-5"
|
||||
aria-label="Unused Storage Information"
|
||||
aria-label={t(
|
||||
"storage.cameraStorage.unusedStorageInformation",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80">
|
||||
<div className="space-y-2">
|
||||
This value represents the total storage used by the recordings
|
||||
in Frigate's database. Frigate does not track storage usage
|
||||
for all files on your disk.
|
||||
</div>
|
||||
<div className="space-y-2">{t("storage.recordings.tips")}</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
@@ -108,7 +111,9 @@ export default function StorageMetrics({
|
||||
/>
|
||||
{earliestDate && (
|
||||
<div className="mt-2 text-xs text-primary-variant">
|
||||
<span className="font-medium">Earliest recording available:</span>{" "}
|
||||
<span className="font-medium">
|
||||
{t("storage.recordings.earliestRecording")}
|
||||
</span>{" "}
|
||||
{formatUnixTimestampToDateTime(earliestDate, {
|
||||
timezone: timezone,
|
||||
strftime_fmt:
|
||||
@@ -135,7 +140,7 @@ export default function StorageMetrics({
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 text-sm font-medium text-muted-foreground">
|
||||
Camera Storage
|
||||
{t("storage.cameraStorage.title")}
|
||||
</div>
|
||||
<div className="mt-4 bg-background_alt p-2.5 md:rounded-2xl">
|
||||
<CombinedStorageGraph
|
||||
|
||||
Reference in New Issue
Block a user