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

This commit is contained in:
ZhaiSoul 2025-03-16 22:21:29 +08:00
parent 52b1c7fbda
commit 78b159ebb1
16 changed files with 66 additions and 40 deletions

View File

@ -12,7 +12,7 @@ import { SearchResult } from "@/types/search";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { TooltipPortal } from "@radix-ui/react-tooltip"; import { TooltipPortal } from "@radix-ui/react-tooltip";
import useContextMenu from "@/hooks/use-contextmenu"; import useContextMenu from "@/hooks/use-contextmenu";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
type SearchThumbnailProps = { type SearchThumbnailProps = {
searchResult: SearchResult; searchResult: SearchResult;
@ -23,6 +23,7 @@ export default function SearchThumbnail({
searchResult, searchResult,
onClick, onClick,
}: SearchThumbnailProps) { }: SearchThumbnailProps) {
const { t } = useTranslation(["views/search"]);
const apiHost = useApiHost(); const apiHost = useApiHost();
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded(); const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();

View File

@ -6,7 +6,7 @@ import { SearchResult } from "@/types/search";
import ActivityIndicator from "../indicators/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
import SearchResultActions from "../menu/SearchResultActions"; import SearchResultActions from "../menu/SearchResultActions";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
type SearchThumbnailProps = { type SearchThumbnailProps = {
searchResult: SearchResult; searchResult: SearchResult;
@ -25,14 +25,15 @@ export default function SearchThumbnailFooter({
showObjectLifecycle, showObjectLifecycle,
showSnapshot, showSnapshot,
}: SearchThumbnailProps) { }: SearchThumbnailProps) {
const { t } = useTranslation(["views/search"]);
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
// date // date
const formattedDate = useFormattedTimestamp( const formattedDate = useFormattedTimestamp(
searchResult.start_time, searchResult.start_time,
config?.ui.time_format == "24hour" config?.ui.time_format == "24hour"
? t("time.formattedTimestampExcludeSeconds.24hour") ? t("time.formattedTimestampExcludeSeconds.24hour", { ns: "common" })
: t("time.formattedTimestampExcludeSeconds"), : t("time.formattedTimestampExcludeSeconds", { ns: "common" }),
config?.ui.timezone, config?.ui.timezone,
); );

View File

@ -14,7 +14,6 @@ import { DateRangePicker } from "../ui/calendar-range";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
import { useState } from "react"; import { useState } from "react";
import PlatformAwareDialog from "../overlay/dialog/PlatformAwareDialog"; import PlatformAwareDialog from "../overlay/dialog/PlatformAwareDialog";
import { t } from "i18next";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
type CalendarFilterButtonProps = { type CalendarFilterButtonProps = {
@ -98,12 +97,13 @@ export function CalendarRangeFilterButton({
defaultText, defaultText,
updateSelectedRange, updateSelectedRange,
}: CalendarRangeFilterButtonProps) { }: CalendarRangeFilterButtonProps) {
const { t } = useTranslation(["components/filter"]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const selectedDate = useFormattedRange( const selectedDate = useFormattedRange(
range?.from == undefined ? 0 : range.from.getTime() / 1000 + 1, range?.from == undefined ? 0 : range.from.getTime() / 1000 + 1,
range?.to == undefined ? 0 : range.to.getTime() / 1000 - 1, range?.to == undefined ? 0 : range.to.getTime() / 1000 - 1,
t("time.formattedTimestampOnlyMonthAndDay"), t("time.formattedTimestampOnlyMonthAndDay", { ns: "common" }),
); );
const trigger = ( const trigger = (

View File

@ -10,7 +10,6 @@ import { DropdownMenuSeparator } from "../ui/dropdown-menu";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import FilterSwitch from "./FilterSwitch"; import FilterSwitch from "./FilterSwitch";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { t } from "i18next";
type LogSettingsButtonProps = { type LogSettingsButtonProps = {
selectedLabels?: LogSeverity[]; selectedLabels?: LogSeverity[];
@ -101,6 +100,7 @@ export function GeneralFilterContent({
selectedLabels, selectedLabels,
updateLabelFilter, updateLabelFilter,
}: GeneralFilterContentProps) { }: GeneralFilterContentProps) {
const { t } = useTranslation(["components/filter"]);
return ( return (
<> <>
<div className="scrollbar-container h-auto overflow-y-auto overflow-x-hidden"> <div className="scrollbar-container h-auto overflow-y-auto overflow-x-hidden">

View File

@ -20,18 +20,19 @@ import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer";
import { DialogClose } from "../ui/dialog"; import { DialogClose } from "../ui/dialog";
import { LuLogOut, LuSquarePen } from "react-icons/lu"; import { LuLogOut, LuSquarePen } from "react-icons/lu";
import useSWR from "swr"; import useSWR from "swr";
import { t } from "i18next";
import { useState } from "react"; import { useState } from "react";
import axios from "axios"; import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import SetPasswordDialog from "../overlay/SetPasswordDialog"; import SetPasswordDialog from "../overlay/SetPasswordDialog";
import { useTranslation } from "react-i18next";
type AccountSettingsProps = { type AccountSettingsProps = {
className?: string; className?: string;
}; };
export default function AccountSettings({ className }: AccountSettingsProps) { export default function AccountSettings({ className }: AccountSettingsProps) {
const { t } = useTranslation(["views/settings"]);
const { data: profile } = useSWR("profile"); const { data: profile } = useSWR("profile");
const { data: config } = useSWR("config"); const { data: config } = useSWR("config");
const logoutUrl = config?.proxy?.logout_url || `${baseUrl}api/logout`; const logoutUrl = config?.proxy?.logout_url || `${baseUrl}api/logout`;
@ -50,12 +51,9 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
.then((response) => { .then((response) => {
if (response.status === 200) { if (response.status === 200) {
setPasswordDialogOpen(false); setPasswordDialogOpen(false);
toast.success( toast.success(t("users.toast.success.updatePassword"), {
t("users.toast.success.updatePassword", { ns: "views/settings" }), position: "top-center",
{ });
position: "top-center",
},
);
} }
}) })
.catch((error) => { .catch((error) => {
@ -65,7 +63,6 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
"Unknown error"; "Unknown error";
toast.error( toast.error(
t("users.toast.error.setPasswordFailed", { t("users.toast.error.setPasswordFailed", {
ns: "views/settings",
errorMessage, errorMessage,
}), }),
{ {
@ -94,7 +91,7 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
</TooltipTrigger> </TooltipTrigger>
<TooltipPortal> <TooltipPortal>
<TooltipContent side="right"> <TooltipContent side="right">
<p>{t("menu.user.account")}</p> <p>{t("menu.user.account", { ns: "common" })}</p>
</TooltipContent> </TooltipContent>
</TooltipPortal> </TooltipPortal>
</Tooltip> </Tooltip>
@ -107,9 +104,12 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
<div className="scrollbar-container w-full flex-col overflow-y-auto overflow-x-hidden"> <div className="scrollbar-container w-full flex-col overflow-y-auto overflow-x-hidden">
<DropdownMenuLabel> <DropdownMenuLabel>
{t("menu.user.current", { {t("menu.user.current", {
user: profile?.username || t("menu.user.anonymous"), ns: "common",
user:
profile?.username || t("menu.user.anonymous", { ns: "common" }),
})}{" "} })}{" "}
{t("role." + profile?.role) && `(${t("role." + profile?.role)})`} {t("role." + profile?.role) &&
`(${t("role." + profile?.role, { ns: "common" })})`}
</DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuSeparator className={isDesktop ? "mt-3" : "mt-1"} /> <DropdownMenuSeparator className={isDesktop ? "mt-3" : "mt-1"} />
{profile?.username && profile.username !== "anonymous" && ( {profile?.username && profile.username !== "anonymous" && (
@ -117,22 +117,22 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
className={ className={
isDesktop ? "cursor-pointer" : "flex items-center p-2 text-sm" isDesktop ? "cursor-pointer" : "flex items-center p-2 text-sm"
} }
aria-label={t("menu.user.setPassword")} aria-label={t("menu.user.setPassword", { ns: "common" })}
onClick={() => setPasswordDialogOpen(true)} onClick={() => setPasswordDialogOpen(true)}
> >
<LuSquarePen className="mr-2 size-4" /> <LuSquarePen className="mr-2 size-4" />
<span>{t("menu.user.setPassword")}</span> <span>{t("menu.user.setPassword", { ns: "common" })}</span>
</MenuItem> </MenuItem>
)} )}
<MenuItem <MenuItem
className={ className={
isDesktop ? "cursor-pointer" : "flex items-center p-2 text-sm" isDesktop ? "cursor-pointer" : "flex items-center p-2 text-sm"
} }
aria-label={t("menu.user.logout")} aria-label={t("menu.user.logout", { ns: "common" })}
> >
<a className="flex" href={logoutUrl}> <a className="flex" href={logoutUrl}>
<LuLogOut className="mr-2 size-4" /> <LuLogOut className="mr-2 size-4" />
<span>{t("menu.user.logout")}</span> <span>{t("menu.user.logout", { ns: "common" })}</span>
</a> </a>
</MenuItem> </MenuItem>
</div> </div>

View File

@ -54,7 +54,6 @@ import { TooltipPortal } from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import useSWR from "swr"; import useSWR from "swr";
import RestartDialog from "../overlay/dialog/RestartDialog"; import RestartDialog from "../overlay/dialog/RestartDialog";
import { t } from "i18next";
import { useLanguage } from "@/context/language-provider"; import { useLanguage } from "@/context/language-provider";
import { useIsAdmin } from "@/hooks/use-is-admin"; import { useIsAdmin } from "@/hooks/use-is-admin";
@ -62,12 +61,14 @@ import SetPasswordDialog from "../overlay/SetPasswordDialog";
import { toast } from "sonner"; import { toast } from "sonner";
import axios from "axios"; import axios from "axios";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { useTranslation } from "react-i18next";
type GeneralSettingsProps = { type GeneralSettingsProps = {
className?: string; className?: string;
}; };
export default function GeneralSettings({ className }: GeneralSettingsProps) { export default function GeneralSettings({ className }: GeneralSettingsProps) {
const { t } = useTranslation(["common"]);
const { data: profile } = useSWR("profile"); const { data: profile } = useSWR("profile");
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const logoutUrl = config?.proxy?.logout_url || "/api/logout"; const logoutUrl = config?.proxy?.logout_url || "/api/logout";

View File

@ -5,7 +5,7 @@ import { IoMdArrowRoundBack } from "react-icons/io";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { isPWA } from "@/utils/isPWA"; import { isPWA } from "@/utils/isPWA";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
const MobilePageContext = createContext<{ const MobilePageContext = createContext<{
open: boolean; open: boolean;
@ -139,6 +139,7 @@ export function MobilePageHeader({
onClose, onClose,
...props ...props
}: MobilePageHeaderProps) { }: MobilePageHeaderProps) {
const { t } = useTranslation(["common"]);
const context = useContext(MobilePageContext); const context = useContext(MobilePageContext);
if (!context) if (!context)
throw new Error("MobilePageHeader must be used within MobilePage"); throw new Error("MobilePageHeader must be used within MobilePage");

View File

@ -9,7 +9,7 @@ import { TooltipPortal } from "@radix-ui/react-tooltip";
import { NavData } from "@/types/navigation"; import { NavData } from "@/types/navigation";
import { IconType } from "react-icons"; import { IconType } from "react-icons";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
const variants = { const variants = {
primary: { primary: {
@ -35,6 +35,7 @@ export default function NavItem({
Icon, Icon,
onClick, onClick,
}: NavItemProps) { }: NavItemProps) {
const { t } = useTranslation(["common"]);
if (item.enabled == false) { if (item.enabled == false) {
return; return;
} }

View File

@ -3,7 +3,7 @@ import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { FaVideo } from "react-icons/fa"; import { FaVideo } from "react-icons/fa";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
type MobileCameraDrawerProps = { type MobileCameraDrawerProps = {
allCameras: string[]; allCameras: string[];
@ -15,6 +15,7 @@ export default function MobileCameraDrawer({
selected, selected,
onSelectCamera, onSelectCamera,
}: MobileCameraDrawerProps) { }: MobileCameraDrawerProps) {
const { t } = useTranslation(["common"]);
const [cameraDrawer, setCameraDrawer] = useState(false); const [cameraDrawer, setCameraDrawer] = useState(false);
if (!isMobile) { if (!isMobile) {

View File

@ -21,7 +21,7 @@ import { cn } from "@/lib/utils";
import { InProgressPreview, VideoPreview } from "../preview/ScrubbablePreview"; import { InProgressPreview, VideoPreview } from "../preview/ScrubbablePreview";
import { Preview } from "@/types/preview"; import { Preview } from "@/types/preview";
import { baseUrl } from "@/api/baseUrl"; import { baseUrl } from "@/api/baseUrl";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
type PreviewPlayerProps = { type PreviewPlayerProps = {
review: ReviewSegment; review: ReviewSegment;
@ -42,6 +42,7 @@ export default function PreviewThumbnailPlayer({
onClick, onClick,
onTimeUpdate, onTimeUpdate,
}: PreviewPlayerProps) { }: PreviewPlayerProps) {
const { t } = useTranslation(["components/player"]);
const apiHost = useApiHost(); const apiHost = useApiHost();
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded(); const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();
@ -169,8 +170,8 @@ export default function PreviewThumbnailPlayer({
const formattedDate = useFormattedTimestamp( const formattedDate = useFormattedTimestamp(
review.start_time, review.start_time,
config?.ui.time_format == "24hour" config?.ui.time_format == "24hour"
? t("time.formattedTimestampExcludeSeconds.24hour") ? t("time.formattedTimestampExcludeSeconds.24hour", { ns: "common" })
: t("time.formattedTimestampExcludeSeconds"), : t("time.formattedTimestampExcludeSeconds", { ns: "common" }),
config?.ui?.timezone, config?.ui?.timezone,
); );

View File

@ -12,7 +12,9 @@ import {
import { Switch } from "./switch"; import { Switch } from "./switch";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { LuCheck } from "react-icons/lu"; import { LuCheck } from "react-icons/lu";
import { t } from "i18next"; import { useTranslation } from "react-i18next";
const { t } = useTranslation(["common"]);
export interface DateRangePickerProps { export interface DateRangePickerProps {

View File

@ -3,7 +3,9 @@ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { ButtonProps, buttonVariants } from "@/components/ui/button" import { ButtonProps, buttonVariants } from "@/components/ui/button"
import { t } from "i18next" import { useTranslation } from "react-i18next"
const { t } = useTranslation(["common"])
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav <nav

View File

@ -1,12 +1,13 @@
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import { t } from "i18next";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { FaExclamationTriangle } from "react-icons/fa"; import { FaExclamationTriangle } from "react-icons/fa";
export default function AccessDenied() { export default function AccessDenied() {
const { t } = useTranslation(["common"]);
useEffect(() => { useEffect(() => {
document.title = t("accessDenied.documentTitle"); document.title = t("accessDenied.documentTitle");
}, []); }, [t]);
return ( return (
<div className="flex min-h-screen flex-col items-center justify-center text-center"> <div className="flex min-h-screen flex-col items-center justify-center text-center">

View File

@ -1,11 +1,12 @@
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import { t } from "i18next";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next";
function NoMatch() { function NoMatch() {
const { t } = useTranslation(["common"]);
useEffect(() => { useEffect(() => {
document.title = t("notFound.documentTitle"); document.title = t("notFound.documentTitle");
}, []); }, [t]);
return ( return (
<> <>

View File

@ -5,7 +5,6 @@ import { Button } from "@/components/ui/button";
import { TooltipProvider } from "@/components/ui/tooltip"; import { TooltipProvider } from "@/components/ui/tooltip";
import { useResizeObserver } from "@/hooks/resize-observer"; import { useResizeObserver } from "@/hooks/resize-observer";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { t } from "i18next";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { import {
isDesktop, isDesktop,
@ -15,6 +14,7 @@ import {
isSafari, isSafari,
useMobileOrientation, useMobileOrientation,
} from "react-device-detect"; } from "react-device-detect";
import { useTranslation } from "react-i18next";
import { FaCompress, FaExpand } from "react-icons/fa"; import { FaCompress, FaExpand } from "react-icons/fa";
import { IoMdArrowBack } from "react-icons/io"; import { IoMdArrowBack } from "react-icons/io";
import { LuPictureInPicture } from "react-icons/lu"; import { LuPictureInPicture } from "react-icons/lu";
@ -33,6 +33,7 @@ export default function LiveBirdseyeView({
fullscreen, fullscreen,
toggleFullscreen, toggleFullscreen,
}: LiveBirdseyeViewProps) { }: LiveBirdseyeViewProps) {
const { t } = useTranslation(["views/live"]);
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const navigate = useNavigate(); const navigate = useNavigate();
const { isPortrait } = useMobileOrientation(); const { isPortrait } = useMobileOrientation();
@ -151,7 +152,9 @@ export default function LiveBirdseyeView({
> >
<IoMdArrowBack className="size-5" /> <IoMdArrowBack className="size-5" />
{isDesktop && ( {isDesktop && (
<div className="text-primary">{t("button.back")}</div> <div className="text-primary">
{t("button.back", { ns: "common" })}
</div>
)} )}
</Button> </Button>
) : ( ) : (
@ -167,7 +170,11 @@ export default function LiveBirdseyeView({
variant={fullscreen ? "overlay" : "primary"} variant={fullscreen ? "overlay" : "primary"}
Icon={fullscreen ? FaCompress : FaExpand} Icon={fullscreen ? FaCompress : FaExpand}
isActive={fullscreen} isActive={fullscreen}
title={fullscreen ? "Close" : "Fullscreen"} title={
fullscreen
? t("button.close", { ns: "common" })
: t("button.fullscreen", { ns: "common" })
}
onClick={toggleFullscreen} onClick={toggleFullscreen}
/> />
)} )}
@ -177,7 +184,11 @@ export default function LiveBirdseyeView({
variant={fullscreen ? "overlay" : "primary"} variant={fullscreen ? "overlay" : "primary"}
Icon={LuPictureInPicture} Icon={LuPictureInPicture}
isActive={pip} isActive={pip}
title={pip ? "Close" : "Picture in Picture"} title={
pip
? t("button.close", { ns: "common" })
: t("button.pictureInPicture", { ns: "common" })
}
onClick={() => { onClick={() => {
if (!pip) { if (!pip) {
setPip(true); setPip(true);

View File

@ -41,9 +41,9 @@ import {
import { FaCompress, FaExpand } from "react-icons/fa"; import { FaCompress, FaExpand } from "react-icons/fa";
import useCameraLiveMode from "@/hooks/use-camera-live-mode"; import useCameraLiveMode from "@/hooks/use-camera-live-mode";
import { useResizeObserver } from "@/hooks/resize-observer"; import { useResizeObserver } from "@/hooks/resize-observer";
import { t } from "i18next";
import LiveContextMenu from "@/components/menu/LiveContextMenu"; import LiveContextMenu from "@/components/menu/LiveContextMenu";
import { useStreamingSettings } from "@/context/streaming-settings-provider"; import { useStreamingSettings } from "@/context/streaming-settings-provider";
import { useTranslation } from "react-i18next";
type LiveDashboardViewProps = { type LiveDashboardViewProps = {
cameras: CameraConfig[]; cameras: CameraConfig[];
@ -61,6 +61,8 @@ export default function LiveDashboardView({
fullscreen, fullscreen,
toggleFullscreen, toggleFullscreen,
}: LiveDashboardViewProps) { }: LiveDashboardViewProps) {
const { t } = useTranslation(["views/live"]);
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
// layout // layout