feat: add Player i18n keys

This commit is contained in:
ZhaiSoul 2025-03-13 22:37:05 +08:00
parent 54ff88917d
commit 412cde0656
4 changed files with 82 additions and 20 deletions

View File

@ -5,5 +5,27 @@
"submitFrigatePlus": {
"title": "Submit this frame to Frigate+?",
"submit": "Submit"
},
"livePlayerRequiredIOSVersion": "iOS 17.1 or greater is required for this live stream type.",
"streamOffline": {
"title": "Stream Offline",
"desc": "No frames have been received on the {{cameraName}} <code>detect</code> stream, check error logs"
},
"cameraDisabled": "Camera is disabled",
"stats": {
"streamType": "Stream Type:",
"streamType.short": "Type",
"bandwidth": "Bandwidth:",
"bandwidth.short": "Bandwidth",
"latency": "Latency:",
"latency.short": "Latency",
"latency.value": "{{secounds}} seconds",
"latency.short.value": "{{secounds}} sec",
"totalFrames": "Total Frames:",
"droppedFrames": "Dropped Frames:",
"droppedFrames.short": "Dropped",
"droppedFrames.short.value": "{{droppedFrames}} frames",
"decodedFrames": "Decoded Frames:",
"droppedFrameRate": "Dropped Frame Rate:"
}
}

View File

@ -5,5 +5,27 @@
"submitFrigatePlus": {
"title": "提交此帧到 Frigate+",
"submit": "提交"
},
"livePlayerRequiredIOSVersion": "此直播流类型需要 iOS 17.1 或更高版本。",
"streamOffline": {
"title": "视频流离线",
"desc": "未在 {{cameraName}} 的 <code>detect</code> 流上接收到任何帧,请检查错误日志"
},
"cameraDisabled": "摄像机已禁用",
"stats": {
"streamType": "流类型:",
"streamType.short": "类型",
"bandwidth": "带宽:",
"bandwidth.short": "带宽",
"latency": "延迟:",
"latency.short": "延迟",
"latency.value": "{{secounds}} 秒",
"latency.short.value": "{{secounds}} 秒",
"totalFrames": "总帧数:",
"droppedFrames": "丢帧数:",
"droppedFrames.short": "丢帧",
"droppedFrames.short.value": "{{droppedFrames}} 帧",
"decodedFrames": "解码帧数:",
"droppedFrameRate": "丢帧率:"
}
}

View File

@ -23,6 +23,7 @@ import { TooltipPortal } from "@radix-ui/react-tooltip";
import { baseUrl } from "@/api/baseUrl";
import { PlayerStats } from "./PlayerStats";
import { LuVideoOff } from "react-icons/lu";
import { Trans, useTranslation } from "react-i18next";
type LivePlayerProps = {
cameraRef?: (ref: HTMLDivElement | null) => void;
@ -71,6 +72,8 @@ export default function LivePlayer({
onError,
onResetLiveMode,
}: LivePlayerProps) {
const { t } = useTranslation(["components/player"]);
const internalContainerRef = useRef<HTMLDivElement | null>(null);
// stats
@ -272,7 +275,7 @@ export default function LivePlayer({
} else {
player = (
<div className="w-5xl text-center text-sm">
iOS 17.1 or greater is required for this live stream type.
{t("livePlayerRequiredIOSVersion")}
</div>
);
}
@ -400,12 +403,17 @@ export default function LivePlayer({
{offline && !showStillWithoutActivity && cameraEnabled && (
<div className="absolute inset-0 left-1/2 top-1/2 flex h-96 w-96 -translate-x-1/2 -translate-y-1/2">
<div className="flex flex-col items-center justify-center rounded-lg bg-background/50 p-5">
<p className="my-5 text-lg">Stream offline</p>
<p className="my-5 text-lg">{t("streamOffline.title")}</p>
<TbExclamationCircle className="mb-3 size-10" />
<p className="max-w-96 text-center">
No frames have been received on the{" "}
{capitalizeFirstLetter(cameraConfig.name)} <code>detect</code>{" "}
stream, check error logs
<Trans
values={{
cameraName: capitalizeFirstLetter(cameraConfig.name),
ns: "components/player",
}}
>
streamOffline.desc
</Trans>
</p>
</div>
</div>
@ -416,7 +424,7 @@ export default function LivePlayer({
<div className="flex h-32 flex-col items-center justify-center rounded-lg p-4 md:h-48 md:w-48">
<LuVideoOff className="mb-2 size-8 md:size-10" />
<p className="max-w-32 text-center text-sm md:max-w-40 md:text-base">
Camera is disabled
{t("cameraDisabled")}
</p>
</div>
</div>

View File

@ -1,5 +1,6 @@
import { cn } from "@/lib/utils";
import { PlayerStatsType } from "@/types/live";
import { useTranslation } from "react-i18next";
type PlayerStatsProps = {
stats: PlayerStatsType;
@ -7,45 +8,46 @@ type PlayerStatsProps = {
};
export function PlayerStats({ stats, minimal }: PlayerStatsProps) {
const { t } = useTranslation(["components/player"]);
const fullStatsContent = (
<>
<p>
<span className="text-white/70">Stream Type:</span>{" "}
<span className="text-white/70">{t("stats.streamType")}</span>{" "}
<span className="text-white">{stats.streamType}</span>
</p>
<p>
<span className="text-white/70">Bandwidth:</span>{" "}
<span className="text-white/70">{t("stats.bandwidth")}</span>{" "}
<span className="text-white">{stats.bandwidth.toFixed(2)} kbps</span>
</p>
{stats.latency != undefined && (
<p>
<span className="text-white/70">Latency:</span>{" "}
<span className="text-white/70">{t("stats.latency")}</span>{" "}
<span
className={`text-white ${stats.latency > 2 ? "text-danger" : ""}`}
>
{stats.latency.toFixed(2)} seconds
{t("stats.latency.value", { secounds: stats.latency.toFixed(2) })}
</span>
</p>
)}
<p>
<span className="text-white/70">Total Frames:</span>{" "}
<span className="text-white/70">{t("stats.totalFrames")}</span>{" "}
<span className="text-white">{stats.totalFrames}</span>
</p>
{stats.droppedFrames != undefined && (
<p>
<span className="text-white/70">Dropped Frames:</span>{" "}
<span className="text-white/70">{t("stats.droppedFrames")}</span>{" "}
<span className="text-white">{stats.droppedFrames}</span>
</p>
)}
{stats.decodedFrames != undefined && (
<p>
<span className="text-white/70">Decoded Frames:</span>{" "}
<span className="text-white/70">{t("stats.decodedFrames")}</span>{" "}
<span className="text-white">{stats.decodedFrames}</span>
</p>
)}
{stats.droppedFrameRate != undefined && (
<p>
<span className="text-white/70">Dropped Frame Rate:</span>{" "}
<span className="text-white/70">{t("stats.droppedFrameRate")}</span>{" "}
<span className="text-white">
{stats.droppedFrameRate.toFixed(2)}%
</span>
@ -57,27 +59,35 @@ export function PlayerStats({ stats, minimal }: PlayerStatsProps) {
const minimalStatsContent = (
<div className="flex flex-row items-center justify-center gap-4">
<div className="flex flex-col items-center justify-start gap-1">
<span className="text-white/70">Type</span>
<span className="text-white/70">{t("stats.streamType.short")}</span>
<span className="text-white">{stats.streamType}</span>
</div>
<div className="flex flex-col items-center gap-1">
<span className="text-white/70">Bandwidth</span>{" "}
<span className="text-white/70">{t("stats.bandwidth.short")}</span>{" "}
<span className="text-white">{stats.bandwidth.toFixed(2)} kbps</span>
</div>
{stats.latency != undefined && (
<div className="hidden flex-col items-center gap-1 md:flex">
<span className="text-white/70">Latency</span>
<span className="text-white/70">{t("stats.latency.short")}</span>
<span
className={`text-white ${stats.latency >= 2 ? "text-danger" : ""}`}
>
{stats.latency.toFixed(2)} sec
{t("stats.latency.short.value", {
secounds: stats.latency.toFixed(2),
})}
</span>
</div>
)}
{stats.droppedFrames != undefined && (
<div className="flex flex-col items-center justify-end gap-1">
<span className="text-white/70">Dropped</span>
<span className="text-white">{stats.droppedFrames} frames</span>
<span className="text-white/70">
{t("stats.droppedFrames.short")}
</span>
<span className="text-white">
{t("stats.droppedFrames.short.value", {
droppedFrames: stats.droppedFrames,
})}
</span>
</div>
)}
</div>