add more translation

This commit is contained in:
ZhaiSoul 2024-12-26 22:22:38 +08:00
parent f45242d22c
commit 2ace4b896a
12 changed files with 356 additions and 52 deletions

View File

@ -1,12 +1,106 @@
{
"object.person": "Person",
"object.cat": "Cat",
"object.bicycle": "Bicycle",
"object.car": "Car",
"object.motorcycle": "Motorcycle",
"object.airplane": "Airplane",
"object.bus": "Bus",
"object.train": "Train",
"object.boat": "Boat",
"object.traffic_light": "Traffic Light",
"object.fire_hydrant": "Fire Hydrant",
"object.street_sign": "Street Sign",
"object.stop_sign": "Stop Sign",
"object.parking_meter": "Parking Meter",
"object.bench": "Bench",
"object.bird": "Bird",
"object.cat": "Cat",
"object.dog": "Dog",
"object.horse": "Horse",
"object.sheep": "Sheep",
"object.cow": "Cow",
"object.elephant": "Elephant",
"object.bear": "Bear",
"object.zebra": "Zebra",
"object.giraffe": "Giraffe",
"object.hat": "Hat",
"object.backpack": "Backpack",
"object.umbrella": "Umbrella",
"object.shoe": "Shoe",
"object.eye_glasses": "Eye Glasses",
"object.handbag": "Handbag",
"object.tie": "Tie",
"object.suitcase": "Suitcase",
"object.frisbee": "Frisbee",
"object.skis": "Skis",
"object.snowboard": "Snowboard",
"object.sports_ball": "Sports Ball",
"object.kite": "Kite",
"object.baseball_bat": "Baseball Bat",
"object.baseball_glove": "Baseball Glove",
"object.skateboard": "Skateboard",
"object.surfboard": "Surfboard",
"object.tennis_racket": "Tennis Racket",
"object.bottle": "Bottle",
"object.plate": "Plate",
"object.wine_glass": "Wine Glass",
"object.cup": "Cup",
"object.fork": "Fork",
"object.knife": "Knife",
"object.spoon": "Spoon",
"object.bowl": "Bowl",
"object.banana": "Banana",
"object.apple": "Apple",
"object.sandwich": "Sandwich",
"object.orange": "Orange",
"object.broccoli": "Broccoli",
"object.carrot": "Carrot",
"object.hot_dog": "Hot Dog",
"object.pizza": "Pizza",
"object.donut": "Donut",
"object.cake": "Cake",
"object.chair": "Chair",
"object.couch": "Couch",
"object.potted_plant": "Potted Plant",
"object.bed": "Bed",
"object.mirror": "Mirror",
"object.dining_table": "Dining Table",
"object.window": "Window",
"object.desk": "Desk",
"object.toilet": "Toilet",
"object.door": "Door",
"object.tv": "TV",
"object.laptop": "Laptop",
"object.mouse": "Mouse",
"object.remote": "Remote",
"object.keyboard": "Keyboard",
"object.cell_phone": "Cell Phone",
"object.microwave": "Microwave",
"object.oven": "Oven",
"object.toaster": "Toaster",
"object.sink": "Sink",
"object.refrigerator": "Refrigerator",
"object.blender": "Blender",
"object.book": "Book",
"object.clock": "Clock",
"object.vase": "Vase",
"object.scissors": "Scissors",
"object.teddy_bear": "Teddy Bear",
"object.hair_dryer": "Hair Dryer",
"object.toothbrush": "Toothbrush",
"object.hair_brush": "Hair Brush",
"ui.time.justNow": "Just now",
"ui.dialog.restart.title": "Are you sure you want to restart Frigate?",
"ui.dialog.restart.button": "Restart",
"ui.dialog.restart.restarting.title": "Frigate is Restarting",
"ui.dialog.restart.restarting.content": "This page will reload in {{countdown}} seconds.",
"ui.dialog.restart.restarting.button": "Force Reload Now",
"ui.stats.ffmpegHighCpuUsage": "{{camera}} has high FFMPEG CPU usage ({{ffmpegAvg}}%)",
"ui.stats.detectHighCpuUsage": "{{camera}} has high detect CPU usage ({{detectAvg}}%)",
"ui.stats.healthy": "System is healthy",
"ui.system.general": "General",
"ui.system.storage": "Storage",
@ -116,7 +210,38 @@
"ui.save": "Save",
"ui.saving": "Saving...",
"ui.cancel": "Cancel",
"ui.close": "Close",
"ui.copy": "Copy",
"ui.back": "Back",
"ui.history": "History",
"ui.fullscreen": "Fullscreen",
"ui.pictureInPicture": "Picture in Picture",
"ui.on": "ON",
"ui.off": "OFF",
"ui.delete": "Delete",
"ui.live.twoWayTalk.enable": "Enable Two Way Talk",
"ui.live.twoWayTalk.disable": "Disable Two Way Talk",
"ui.live.cameraAudio.enable": "Enable Camera Audio",
"ui.live.cameraAudio.disable": "Disable Camera Audio",
"ui.live.ptz.move.left.label": "Move PTZ camera to the left",
"ui.live.ptz.move.up.label": "Move PTZ camera up",
"ui.live.ptz.move.down.label": "Move PTZ camera down",
"ui.live.ptz.move.right.label": "Move PTZ camera to the right",
"ui.live.ptz.zoom.in.label": "Zoom PTZ camera in",
"ui.live.ptz.zoom.out.label": "Zoom PTZ camera out",
"ui.live.ptz.frame.center.label": "Click in the frame to center the PTZ camera",
"ui.live.detect.enable": "Enable Detect",
"ui.live.detect.disable": "Disable Detect",
"ui.live.recording.enable": "Enable Recording",
"ui.live.recording.disable": "Disable Recording",
"ui.live.snapshots.enable": "Enable Snapshots",
"ui.live.snapshots.disable": "Disable Snapshots",
"ui.live.audioDetect.enable": "Enable Audio Detect",
"ui.live.audioDetect.disable": "Disable Audio Detect",
"ui.live.autotracking.enable": "Enable Autotracking",
"ui.live.autotracking.disable": "Disable Autotracking",
"ui.calendarFilter.last24Hours": "Last 24 Hours",
@ -256,6 +381,27 @@
"ui.settingView.debug.regions.desc": "Show a box of the region of interest sent to the object detector",
"ui.settingView.debug.regions.tips": "<p className=\"mb-2\"><strong>Region Boxes</strong></p><br><p>Bright green boxes will be overlaid on areas of interest in the frame that are being sent to the object detector.</p>",
"ui.settingView.users": "Users",
"ui.settingView.users.addUser": "Add User",
"ui.settingView.users.updatePassword": "Update Password",
"ui.settingView.users.dialog.createUser": "Create User",
"ui.settingView.users.dialog.createUser.user": "User",
"ui.settingView.users.dialog.createUser.password": "Password",
"ui.settingView.users.dialog.deleteUser": "Delete User",
"ui.settingView.users.dialog.deleteUser.warn": "Are you sure?",
"ui.settingView.users.dialog.setPassword": "Set Password",
"ui.settingView.notification": "Notifications",
"ui.settingView.notification.notificationSettings": "Notification Settings",
"ui.settingView.notification.desc": "Frigate can natively send push notifications to your device when it is running in the browser or installed as a PWA.",
"ui.settingView.notification.documentation": "Read the Documentation",
"ui.settingView.notification.email": "Email",
"ui.settingView.notification.email.placeholder": "example@email.com",
"ui.settingView.notification.email.desc": "Entering a valid email is required, as this is used by the push server in case problems occur.",
"ui.settingView.notification.registerDevice": "Register for notifications on this device",
"ui.settingView.notification.unregisterDevice": "Unregister for notifications on this device",
"ui.configEditorView.configEditor": "Config Editor",
"ui.configEditorView.copyConfig": "Copy Config",
"ui.configEditorView.saveAndRestart": "Save & Restart",

View File

@ -1,13 +1,107 @@
{
"object.person": "人",
"object.bicycle": "自行车",
"object.car": "汽车",
"object.motorcycle": "摩托车",
"object.airplane": "飞机",
"object.bus": "公交车",
"object.train": "火车",
"object.boat": "船",
"object.traffic_light": "交通灯",
"object.fire_hydrant": "消防栓",
"object.street_sign": "路标",
"object.stop_sign": "停车标志",
"object.parking_meter": "停车计时器",
"object.bench": "长椅",
"object.bird": "鸟",
"object.cat": "猫",
"object.car": "车",
"object.dog": "狗",
"object.horse": "马",
"object.sheep": "羊",
"object.cow": "牛",
"object.elephant": "大象",
"object.bear": "熊",
"object.zebra": "斑马",
"object.giraffe": "长颈鹿",
"object.hat": "帽子",
"object.backpack": "背包",
"object.umbrella": "雨伞",
"object.shoe": "鞋子",
"object.eye_glasses": "眼镜",
"object.handbag": "手提包",
"object.tie": "领带",
"object.suitcase": "手提箱",
"object.frisbee": "飞盘",
"object.skis": "滑雪板",
"object.snowboard": "滑雪板",
"object.sports_ball": "运动球",
"object.kite": "风筝",
"object.baseball_bat": "棒球棒",
"object.baseball_glove": "棒球手套",
"object.skateboard": "滑板",
"object.surfboard": "冲浪板",
"object.tennis_racket": "网球拍",
"object.bottle": "瓶子",
"object.plate": "盘子",
"object.wine_glass": "酒杯",
"object.cup": "杯子",
"object.fork": "叉子",
"object.knife": "刀",
"object.spoon": "勺子",
"object.bowl": "碗",
"object.banana": "香蕉",
"object.apple": "苹果",
"object.sandwich": "三明治",
"object.orange": "橙子",
"object.broccoli": "西兰花",
"object.carrot": "胡萝卜",
"object.hot_dog": "热狗",
"object.pizza": "披萨",
"object.donut": "甜甜圈",
"object.cake": "蛋糕",
"object.chair": "椅子",
"object.couch": "沙发",
"object.potted_plant": "盆栽植物",
"object.bed": "床",
"object.mirror": "镜子",
"object.dining_table": "餐桌",
"object.window": "窗户",
"object.desk": "桌子",
"object.toilet": "厕所",
"object.door": "门",
"object.tv": "电视",
"object.laptop": "笔记本电脑",
"object.mouse": "鼠标",
"object.remote": "遥控器",
"object.keyboard": "键盘",
"object.cell_phone": "手机",
"object.microwave": "微波炉",
"object.oven": "烤箱",
"object.toaster": "烤面包机",
"object.sink": "水槽",
"object.refrigerator": "冰箱",
"object.blender": "搅拌机",
"object.book": "书",
"object.clock": "时钟",
"object.vase": "花瓶",
"object.scissors": "剪刀",
"object.teddy_bear": "泰迪熊",
"object.hair_dryer": "吹风机",
"object.toothbrush": "牙刷",
"object.hair_brush": "发刷",
"ui.time.justNow": "刚才",
"ui.dialog.restart.title": "你确定要重启 Frigate?",
"ui.dialog.restart.button": "重启",
"ui.dialog.restart.restarting.title": "Frigate 正在重启",
"ui.dialog.restart.restarting.content": "该页面将会在 {{countdown}} 秒后自动刷新。",
"ui.dialog.restart.restarting.button": "强制刷新",
"ui.stats.ffmpegHighCpuUsage": "{{camera}} 的 FFMPEG CPU 使用率较高({{ffmpegAvg}}%",
"ui.stats.detectHighCpuUsage": "{{camera}} 的 探测 CPU 使用率较高({{detectAvg}}%",
"ui.stats.healthy": "系统运行正常",
"ui.system.general": "常规",
"ui.system.storage": "存储",
@ -117,7 +211,39 @@
"ui.save": "保存",
"ui.saving": "保存中……",
"ui.cancel": "取消",
"ui.close": "关闭",
"ui.copy": "复制",
"ui.back": "返回",
"ui.history": "历史",
"ui.fullscreen": "全屏",
"ui.exitFullscreen": "退出全屏",
"ui.pictureInPicture": "画中画",
"ui.on": "开",
"ui.off": "关",
"ui.delete": "删除",
"ui.live.twoWayTalk.enable": "开启双向对话",
"ui.live.twoWayTalk.disable": "关闭双向对话",
"ui.live.cameraAudio.enable": "开启摄像头音频",
"ui.live.cameraAudio.disable": "关闭摄像头音频",
"ui.live.ptz.move.left.label": "PTZ摄像头向左移动",
"ui.live.ptz.move.up.label": "PTZ摄像头向上移动",
"ui.live.ptz.move.down.label": "PTZ摄像头向下移动",
"ui.live.ptz.move.right.label": "PTZ摄像头向右移动",
"ui.live.ptz.zoom.in.label": "PTZ摄像头放大",
"ui.live.ptz.zoom.out.label": "PTZ摄像头缩小",
"ui.live.ptz.frame.center.label": "点击将PTZ摄像头画面居中",
"ui.live.detect.enable": "启用检测",
"ui.live.detect.disable": "关闭检测",
"ui.live.recording.enable": "启用录制",
"ui.live.recording.disable": "关闭录制",
"ui.live.snapshots.enable": "启用快照",
"ui.live.snapshots.disable": "关闭快照",
"ui.live.audioDetect.enable": "启用音频检测",
"ui.live.audioDetect.disable": "关闭音频检测",
"ui.live.autotracking.enable": "启用自动追踪",
"ui.live.autotracking.disable": "关闭自动追踪",
"ui.calendarFilter.last24Hours": "过去24小时",
@ -257,6 +383,27 @@
"ui.settingView.debug.regions.desc": "显示发送到运动检测器感兴趣范围的框。",
"ui.settingView.debug.regions.tips": "<p className=\"mb-2\"><strong>范围框</strong></p><br><p>将在帧中发送到目标检测器的感兴趣范围上叠加绿色框。</p>",
"ui.settingView.users": "用户管理",
"ui.settingView.users.addUser": "添加用户",
"ui.settingView.users.updatePassword": "修改密码",
"ui.settingView.users.dialog.createUser": "创建用户",
"ui.settingView.users.dialog.createUser.user": "用户名",
"ui.settingView.users.dialog.createUser.password": "密码",
"ui.settingView.users.dialog.deleteUser": "删除该用户",
"ui.settingView.users.dialog.deleteUser.warn": "你确定要删除该用户吗?",
"ui.settingView.users.dialog.setPassword": "修改密码",
"ui.settingView.notification": "通知",
"ui.settingView.notification.notificationSettings": "通知设置",
"ui.settingView.notification.desc": "Frigate 在浏览器中运行或作为 PWA 安装时,可以原生向您的设备发送推送通知。",
"ui.settingView.notification.documentation": "阅读文档(英文)",
"ui.settingView.notification.email": "电子邮箱",
"ui.settingView.notification.email.placeholder": "例如example@email.com",
"ui.settingView.notification.email.desc": "Frigate 在浏览器中运行或作为 PWA 应用安装时,可以原生向你的设备发送推送通知。",
"ui.settingView.notification.registerDevice": "为这个设备注册通知",
"ui.settingView.notification.unregisterDevice": "取消这个设备注册通知",
"ui.configEditorView.configEditor": "配置编辑器",
"ui.configEditorView.copyConfig": "复制配置",
"ui.configEditorView.saveAndRestart": "保存并重启",

View File

@ -5,6 +5,7 @@ import {
} from "@/context/statusbar-provider";
import useStats, { useAutoFrigateStats } from "@/hooks/use-stats";
import { useContext, useEffect, useMemo } from "react";
import { Trans } from "react-i18next";
import { FaCheck } from "react-icons/fa";
import { IoIosWarning } from "react-icons/io";
import { MdCircle } from "react-icons/md";
@ -129,7 +130,7 @@ export default function Statusbar() {
{Object.entries(messages).length === 0 ? (
<div className="flex items-center gap-2 text-sm">
<FaCheck className="size-3 text-green-500" />
System is healthy
<Trans>ui.stats.healthy</Trans>
</div>
) : (
Object.entries(messages).map(([key, messageArray]) => (

View File

@ -20,6 +20,7 @@ import {
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { Trans } from "react-i18next";
type CreateUserOverlayProps = {
show: boolean;
@ -63,7 +64,7 @@ export default function CreateUserDialog({
<Dialog open={show} onOpenChange={onCancel}>
<DialogContent>
<DialogHeader>
<DialogTitle>Create User</DialogTitle>
<DialogTitle><Trans>ui.settingView.users.dialog.createUser</Trans></DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
@ -71,7 +72,7 @@ export default function CreateUserDialog({
name="user"
render={({ field }) => (
<FormItem>
<FormLabel>User</FormLabel>
<FormLabel><Trans>ui.settingView.users.dialog.createUser.user</Trans></FormLabel>
<FormControl>
<Input
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
@ -86,7 +87,7 @@ export default function CreateUserDialog({
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormLabel><Trans>ui.settingView.users.dialog.createUser.password</Trans></FormLabel>
<FormControl>
<Input
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
@ -104,7 +105,7 @@ export default function CreateUserDialog({
disabled={isLoading}
>
{isLoading && <ActivityIndicator className="mr-2 h-4 w-4" />}
Create User
<Trans>ui.settingView.users.dialog.createUser</Trans>
</Button>
</DialogFooter>
</form>

View File

@ -1,3 +1,4 @@
import { Trans } from "react-i18next";
import { Button } from "../ui/button";
import {
Dialog,
@ -21,9 +22,9 @@ export default function DeleteUserDialog({
<Dialog open={show} onOpenChange={onCancel}>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete User</DialogTitle>
<DialogTitle><Trans>ui.settingView.users.dialog.deleteUser</Trans></DialogTitle>
</DialogHeader>
<div>Are you sure?</div>
<div><Trans>ui.settingView.users.dialog.deleteUser.warn</Trans></div>
<DialogFooter>
<Button
className="flex items-center gap-1"
@ -32,7 +33,7 @@ export default function DeleteUserDialog({
size="sm"
onClick={onDelete}
>
Delete
<Trans>ui.delete</Trans>
</Button>
</DialogFooter>
</DialogContent>

View File

@ -8,6 +8,7 @@ import {
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { Trans } from "react-i18next";
type SetPasswordProps = {
show: boolean;
@ -25,7 +26,7 @@ export default function SetPasswordDialog({
<Dialog open={show} onOpenChange={onCancel}>
<DialogContent onOpenAutoFocus={(e) => e.preventDefault()}>
<DialogHeader>
<DialogTitle>Set Password</DialogTitle>
<DialogTitle><Trans>ui.settingView.users.dialog.setPassword</Trans></DialogTitle>
</DialogHeader>
<Input
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
@ -43,7 +44,7 @@ export default function SetPasswordDialog({
onSave(password!);
}}
>
Save
<Trans>ui.save</Trans>
</Button>
</DialogFooter>
</DialogContent>

View File

@ -18,6 +18,8 @@ import {
import { Button } from "@/components/ui/button";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { baseUrl } from "@/api/baseUrl";
import { t } from "i18next";
import { Trans } from "react-i18next";
type RestartDialogProps = {
isOpen: boolean;
@ -79,13 +81,13 @@ export default function RestartDialog({
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you sure you want to restart Frigate?
<Trans>ui.dialog.restart.title</Trans>
</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogCancel><Trans>ui.cancel</Trans></AlertDialogCancel>
<AlertDialogAction onClick={handleRestart}>
Restart
<Trans>ui.dialog.restart.button</Trans>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
@ -100,10 +102,10 @@ export default function RestartDialog({
<ActivityIndicator />
<SheetHeader className="mt-5 text-center">
<SheetTitle className="text-center">
Frigate is Restarting
<Trans>ui.dialog.restart.restarting.title</Trans>
</SheetTitle>
<SheetDescription className="text-center">
<div>This page will reload in {countdown} seconds.</div>
<div>{t("ui.dialog.restart.restarting.content", {countdown})}</div>
</SheetDescription>
</SheetHeader>
<Button
@ -112,7 +114,7 @@ export default function RestartDialog({
aria-label="Force reload now"
onClick={handleForceReload}
>
Force Reload Now
<Trans>ui.dialog.restart.restarting.button</Trans>
</Button>
</div>
</SheetContent>

View File

@ -42,6 +42,7 @@ import {
} from "@/components/ui/tooltip";
import { Toaster } from "@/components/ui/sonner";
import useCameraLiveMode from "@/hooks/use-camera-live-mode";
import { t } from "i18next";
type DraggableGridLayoutProps = {
cameras: CameraConfig[];
@ -519,7 +520,7 @@ export default function DraggableGridLayout({
</div>
</TooltipTrigger>
<TooltipContent>
{fullscreen ? "Exit Fullscreen" : "Fullscreen"}
{fullscreen ? t("ui.exitFullscreen") : t("ui.fullscreen")}
</TooltipContent>
</Tooltip>
</>

View File

@ -85,6 +85,8 @@ import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import useSWR from "swr";
import { cn } from "@/lib/utils";
import { useSessionPersistence } from "@/hooks/use-session-persistence";
import { Trans } from "react-i18next";
import { t } from "i18next";
type LiveCameraViewProps = {
config?: FrigateConfig;
@ -363,7 +365,7 @@ export default function LiveCameraView({
onClick={() => navigate(-1)}
>
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
{isDesktop && <div className="text-primary">Back</div>}
{isDesktop && <div className="text-primary"><Trans>ui.back</Trans></div>}
</Button>
<Button
className="flex items-center gap-2.5 rounded-lg"
@ -383,7 +385,7 @@ export default function LiveCameraView({
}}
>
<LuHistory className="size-5 text-secondary-foreground" />
{isDesktop && <div className="text-primary">History</div>}
{isDesktop && <div className="text-primary"><Trans>ui.history</Trans></div>}
</Button>
</div>
) : (
@ -412,7 +414,7 @@ export default function LiveCameraView({
variant={fullscreen ? "overlay" : "primary"}
Icon={fullscreen ? FaCompress : FaExpand}
isActive={fullscreen}
title={fullscreen ? "Close" : "Fullscreen"}
title={fullscreen ? t("ui.close") : t("ui.fullscreen")}
onClick={toggleFullscreen}
/>
)}
@ -422,7 +424,7 @@ export default function LiveCameraView({
variant={fullscreen ? "overlay" : "primary"}
Icon={LuPictureInPicture}
isActive={pip}
title={pip ? "Close" : "Picture in Picture"}
title={pip ? t("ui.close") : t("ui.pictureInPicture")}
onClick={() => {
if (!pip) {
setPip(true);
@ -665,7 +667,7 @@ function PtzControlPanel({
{ptz?.features?.includes("pt") && (
<>
<TooltipButton
label="Move camera left"
label={t("ui.live.ptz.move.left.label")}
onMouseDown={(e) => {
e.preventDefault();
sendPtz("MOVE_LEFT");
@ -680,7 +682,7 @@ function PtzControlPanel({
<FaAngleLeft />
</TooltipButton>
<TooltipButton
label="Move camera up"
label={t("ui.live.ptz.move.up.label")}
onMouseDown={(e) => {
e.preventDefault();
sendPtz("MOVE_UP");
@ -695,7 +697,7 @@ function PtzControlPanel({
<FaAngleUp />
</TooltipButton>
<TooltipButton
label="Move camera down"
label={t("ui.live.ptz.move.down.label")}
onMouseDown={(e) => {
e.preventDefault();
sendPtz("MOVE_DOWN");
@ -710,7 +712,7 @@ function PtzControlPanel({
<FaAngleDown />
</TooltipButton>
<TooltipButton
label="Move camera right"
label={t("ui.live.ptz.move.right.label")}
onMouseDown={(e) => {
e.preventDefault();
sendPtz("MOVE_RIGHT");
@ -729,7 +731,7 @@ function PtzControlPanel({
{ptz?.features?.includes("zoom") && (
<>
<TooltipButton
label="Zoom in"
label={t("ui.live.ptz.zoom.in.label")}
onMouseDown={(e) => {
e.preventDefault();
sendPtz("ZOOM_IN");
@ -744,7 +746,7 @@ function PtzControlPanel({
<MdZoomIn />
</TooltipButton>
<TooltipButton
label="Zoom out"
label={t("ui.live.ptz.zoom.out.label")}
onMouseDown={(e) => {
e.preventDefault();
sendPtz("ZOOM_OUT");
@ -847,7 +849,7 @@ function FrigateCameraFeatures({
variant={fullscreen ? "overlay" : "primary"}
Icon={detectState == "ON" ? MdPersonSearch : MdPersonOff}
isActive={detectState == "ON"}
title={`${detectState == "ON" ? "Disable" : "Enable"} Detect`}
title={detectState == "ON" ? t("ui.live.detect.disable") : t("ui.live.detect.enable")}
onClick={() => sendDetect(detectState == "ON" ? "OFF" : "ON")}
/>
<CameraFeatureToggle
@ -855,7 +857,7 @@ function FrigateCameraFeatures({
variant={fullscreen ? "overlay" : "primary"}
Icon={recordState == "ON" ? LuVideo : LuVideoOff}
isActive={recordState == "ON"}
title={`${recordState == "ON" ? "Disable" : "Enable"} Recording`}
title={recordState == "ON" ? t("ui.live.recording.disable") : t("ui.live.recording.enable")}
onClick={() => sendRecord(recordState == "ON" ? "OFF" : "ON")}
/>
<CameraFeatureToggle
@ -863,7 +865,7 @@ function FrigateCameraFeatures({
variant={fullscreen ? "overlay" : "primary"}
Icon={snapshotState == "ON" ? MdPhotoCamera : MdNoPhotography}
isActive={snapshotState == "ON"}
title={`${snapshotState == "ON" ? "Disable" : "Enable"} Snapshots`}
title={snapshotState == "ON" ? t("ui.live.snapshots.disable") : t("ui.live.snapshots.enable")}
onClick={() => sendSnapshot(snapshotState == "ON" ? "OFF" : "ON")}
/>
{audioDetectEnabled && (
@ -872,7 +874,7 @@ function FrigateCameraFeatures({
variant={fullscreen ? "overlay" : "primary"}
Icon={audioState == "ON" ? LuEar : LuEarOff}
isActive={audioState == "ON"}
title={`${audioState == "ON" ? "Disable" : "Enable"} Audio Detect`}
title={audioState == "ON" ? t("ui.live.audioDetect.disable") : t("ui.live.audioDetect.enable")}
onClick={() => sendAudio(audioState == "ON" ? "OFF" : "ON")}
/>
)}
@ -882,7 +884,7 @@ function FrigateCameraFeatures({
variant={fullscreen ? "overlay" : "primary"}
Icon={autotrackingState == "ON" ? TbViewfinder : TbViewfinderOff}
isActive={autotrackingState == "ON"}
title={`${autotrackingState == "ON" ? "Disable" : "Enable"} Autotracking`}
title={autotrackingState == "ON" ? t("ui.live.autotracking.disable") : t("ui.live.autotracking.enable")}
onClick={() =>
sendAutotracking(autotrackingState == "ON" ? "OFF" : "ON")
}

View File

@ -32,6 +32,7 @@ import { LivePlayerError } from "@/types/live";
import { FaCompress, FaExpand } from "react-icons/fa";
import useCameraLiveMode from "@/hooks/use-camera-live-mode";
import { useResizeObserver } from "@/hooks/resize-observer";
import { t } from "i18next";
type LiveDashboardViewProps = {
cameras: CameraConfig[];
@ -387,7 +388,7 @@ export default function LiveDashboardView({
</div>
</TooltipTrigger>
<TooltipContent>
{fullscreen ? "Exit Fullscreen" : "Fullscreen"}
{fullscreen ? t("ui.exitFullscreen") : t("ui.fullscreen")}
</TooltipContent>
</Tooltip>
</div>

View File

@ -15,6 +15,7 @@ import { Card } from "@/components/ui/card";
import { HiTrash } from "react-icons/hi";
import { FaUserEdit } from "react-icons/fa";
import { LuPlus } from "react-icons/lu";
import { Trans } from "react-i18next";
export default function AuthenticationView() {
const { data: config } = useSWR<FrigateConfig>("config");
@ -91,7 +92,7 @@ export default function AuthenticationView() {
<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">
<div className="flex flex-row items-center justify-between gap-2">
<Heading as="h3" className="my-2">
Users
<Trans>ui.settingView.users</Trans>
</Heading>
<Button
className="flex items-center gap-1"
@ -102,7 +103,7 @@ export default function AuthenticationView() {
}}
>
<LuPlus className="text-secondary-foreground" />
Add User
<Trans>ui.settingView.users.addUser</Trans>
</Button>
</div>
<div className="mt-3 space-y-3">
@ -123,7 +124,7 @@ export default function AuthenticationView() {
}}
>
<FaUserEdit />
<div className="hidden md:block">Update Password</div>
<div className="hidden md:block"><Trans>ui.settingView.users.updatePassword</Trans></div>
</Button>
<Button
className="flex items-center gap-1"
@ -135,7 +136,7 @@ export default function AuthenticationView() {
}}
>
<HiTrash />
<div className="hidden md:block">Delete</div>
<div className="hidden md:block"><Trans>ui.delete</Trans></div>
</Button>
</div>
</div>

View File

@ -19,8 +19,10 @@ import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { FrigateConfig } from "@/types/frigateConfig";
import { zodResolver } from "@hookform/resolvers/zod";
import axios from "axios";
import { t } from "i18next";
import { useCallback, useContext, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { Trans } from "react-i18next";
import { LuExternalLink } from "react-icons/lu";
import { Link } from "react-router-dom";
import { toast } from "sonner";
@ -196,14 +198,13 @@ export default function NotificationView({
<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">
<Heading as="h3" className="my-2">
Notification Settings
<Trans>ui.settingView.notification.notificationSettings</Trans>
</Heading>
<div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant">
<p>
Frigate can natively send push notifications to your device when
it is running in the browser or installed as a PWA.
<Trans>ui.settingView.notification.desc</Trans>
</p>
<div className="flex items-center text-primary">
<Link
@ -212,7 +213,7 @@ export default function NotificationView({
rel="noopener noreferrer"
className="inline"
>
Read the Documentation{" "}
<Trans>ui.settingView.notification.documentation</Trans>{" "}
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
@ -232,7 +233,7 @@ export default function NotificationView({
<FormControl>
<div className="flex flex-row items-center justify-start gap-2">
<Label className="cursor-pointer" htmlFor="auto-live">
Notifications
<Trans>ui.settingView.notification</Trans>
</Label>
<Switch
id="auto-live"
@ -251,17 +252,16 @@ export default function NotificationView({
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormLabel><Trans>ui.settingView.notification.email</Trans></FormLabel>
<FormControl>
<Input
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark] md:w-72"
placeholder="example@email.com"
placeholder={t("ui.settingView.notification.email.placeholder")}
{...field}
/>
</FormControl>
<FormDescription>
Entering a valid email is required, as this is used by the
push server in case problems occur.
<Trans>ui.settingView.notification.email.desc</Trans>
</FormDescription>
<FormMessage />
</FormItem>
@ -274,7 +274,7 @@ export default function NotificationView({
onClick={onCancel}
type="button"
>
Cancel
<Trans>ui.cancel</Trans>
</Button>
<Button
variant="select"
@ -286,10 +286,10 @@ export default function NotificationView({
{isLoading ? (
<div className="flex flex-row items-center gap-2">
<ActivityIndicator />
<span>Saving...</span>
<span><Trans>ui.saving</Trans></span>
</div>
) : (
"Save"
<Trans>ui.save</Trans>
)}
</Button>
</div>
@ -336,7 +336,7 @@ export default function NotificationView({
}
}}
>
{`${registration != null ? "Unregister" : "Register"} for notifications on this device`}
{registration != null ? t("ui.settingView.notification.unregisterDevice") : t("ui.settingView.notification.registerDevice")}
</Button>
</div>
</div>