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 10:36:20 -05:00
committed by GitHub
co-authored by Josh Hawkins
parent db541abed4
commit d34533981f
150 changed files with 6809 additions and 1926 deletions
+12 -5
View File
@@ -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=""
+3 -1
View File
@@ -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(
+27 -14
View File
@@ -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}
+17 -12
View File
@@ -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