mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-08-18 13:47:20 +02:00
feat: document locale url (#18409)
* feat: document locale url * fix: remove debug code * fix readme_cn line * feat: add menu document locale url
This commit is contained in:
parent
cbdac9ece5
commit
9b7d4d0a8f
@ -61,4 +61,5 @@
|
||||
|
||||
## 非官方中文讨论社区
|
||||
欢迎加入中文讨论QQ群:1043861059
|
||||
|
||||
Bilibili:https://space.bilibili.com/3546894915602564
|
||||
|
@ -64,12 +64,15 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { supportedLanguageKeys } from "@/lib/const";
|
||||
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type GeneralSettingsProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
||||
const { t } = useTranslation(["common", "views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: profile } = useSWR("profile");
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const logoutUrl = config?.proxy?.logout_url || "/api/logout";
|
||||
@ -479,7 +482,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
||||
{t("menu.help")}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<a href="https://docs.frigate.video" target="_blank">
|
||||
<a href={getLocaleDocUrl("/")} target="_blank">
|
||||
<MenuItem
|
||||
className={
|
||||
isDesktop ? "cursor-pointer" : "flex items-center p-2 text-sm"
|
||||
|
@ -18,6 +18,7 @@ import { LogChip } from "../indicators/Chip";
|
||||
import { useMemo } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type LogInfoDialogProps = {
|
||||
logLine?: LogLine;
|
||||
@ -105,23 +106,25 @@ export default function LogInfoDialog({
|
||||
}
|
||||
|
||||
function useHelpfulLinks(content: string | undefined) {
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
return useMemo(() => {
|
||||
if (!content) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const links = [];
|
||||
|
||||
if (/Could not clear [\d.]* currently [\d.]*/.exec(content)) {
|
||||
links.push({
|
||||
link: "https://docs.frigate.video/configuration/record#will-frigate-delete-old-recordings-if-my-storage-runs-out",
|
||||
link: getLocaleDocUrl(
|
||||
"configuration/record#will-frigate-delete-old-recordings-if-my-storage-runs-out",
|
||||
),
|
||||
text: "Frigate Automatic Storage Cleanup",
|
||||
});
|
||||
}
|
||||
|
||||
if (/Did not detect hwaccel/.exec(content)) {
|
||||
links.push({
|
||||
link: "https://docs.frigate.video/configuration/hardware_acceleration",
|
||||
link: getLocaleDocUrl("configuration/hardware_acceleration"),
|
||||
text: "Setup Hardware Acceleration",
|
||||
});
|
||||
}
|
||||
@ -139,25 +142,27 @@ function useHelpfulLinks(content: string | undefined) {
|
||||
content.includes("No VA display found for device /dev/dri/renderD128")
|
||||
) {
|
||||
links.push({
|
||||
link: "https://docs.frigate.video/configuration/hardware_acceleration",
|
||||
link: getLocaleDocUrl("configuration/hardware_acceleration"),
|
||||
text: "Verify Hardware Acceleration Setup",
|
||||
});
|
||||
}
|
||||
|
||||
if (content.includes("No EdgeTPU was detected")) {
|
||||
links.push({
|
||||
link: "https://docs.frigate.video/troubleshooting/edgetpu",
|
||||
link: getLocaleDocUrl("troubleshooting/edgetpu"),
|
||||
text: "Troubleshoot Coral",
|
||||
});
|
||||
}
|
||||
|
||||
if (content.includes("The current SHM size of")) {
|
||||
links.push({
|
||||
link: "https://docs.frigate.video/frigate/installation/#calculating-required-shm-size",
|
||||
link: getLocaleDocUrl(
|
||||
"frigate/installation/#calculating-required-shm-size",
|
||||
),
|
||||
text: "Calculate Correct SHM Size",
|
||||
});
|
||||
}
|
||||
|
||||
return links;
|
||||
}, [content]);
|
||||
}, [content, getLocaleDocUrl]);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type AnnotationSettingsPaneProps = {
|
||||
event: Event;
|
||||
@ -43,6 +44,7 @@ export function AnnotationSettingsPane({
|
||||
setAnnotationOffset,
|
||||
}: AnnotationSettingsPaneProps) {
|
||||
const { t } = useTranslation(["views/explore"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
@ -180,7 +182,7 @@ export function AnnotationSettingsPane({
|
||||
</Trans>
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/reference"
|
||||
to={getLocaleDocUrl("configuration/reference")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -24,6 +24,7 @@ import { Trans, useTranslation } from "react-i18next";
|
||||
import { LuExternalLink } from "react-icons/lu";
|
||||
import { Link } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
const STEPS = ["steps.faceName", "steps.uploadFace", "steps.nextSteps"];
|
||||
|
||||
@ -38,7 +39,7 @@ export default function CreateFaceWizardDialog({
|
||||
onFinish,
|
||||
}: CreateFaceWizardDialogProps) {
|
||||
const { t } = useTranslation("views/faceLibrary");
|
||||
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
// wizard
|
||||
|
||||
const [step, setStep] = useState(0);
|
||||
@ -155,7 +156,7 @@ export default function CreateFaceWizardDialog({
|
||||
</p>
|
||||
<div className="my-2 flex items-center text-sm text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/face_recognition"
|
||||
to={getLocaleDocUrl("configuration/face_recognition")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -32,6 +32,7 @@ import { LuCheck, LuExternalLink, LuInfo, LuX } from "react-icons/lu";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LiveStreamMetadata } from "@/types/live";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type CameraStreamingDialogProps = {
|
||||
camera: string;
|
||||
@ -51,6 +52,8 @@ export function CameraStreamingDialog({
|
||||
onSave,
|
||||
}: CameraStreamingDialogProps) {
|
||||
const { t } = useTranslation(["components/camera", "components/dialog"]);
|
||||
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -220,7 +223,7 @@ export function CameraStreamingDialog({
|
||||
})}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
to={getLocaleDocUrl("configuration/live")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -281,7 +284,7 @@ export function CameraStreamingDialog({
|
||||
{t("group.camera.setting.audio.tips.title")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
to={getLocaleDocUrl("configuration/live")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -23,6 +23,7 @@ import ActivityIndicator from "../indicators/activity-indicator";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LuExternalLink } from "react-icons/lu";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type MotionMaskEditPaneProps = {
|
||||
polygons?: Polygon[];
|
||||
@ -52,6 +53,7 @@ export default function MotionMaskEditPane({
|
||||
setSnapPoints,
|
||||
}: MotionMaskEditPaneProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
|
||||
@ -249,7 +251,7 @@ export default function MotionMaskEditPane({
|
||||
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/masks/"
|
||||
to={getLocaleDocUrl("configuration/masks/")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -32,6 +32,7 @@ import { getAttributeLabels } from "@/utils/iconUtil";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LuExternalLink } from "react-icons/lu";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type ZoneEditPaneProps = {
|
||||
polygons?: Polygon[];
|
||||
@ -63,6 +64,7 @@ export default function ZoneEditPane({
|
||||
setSnapPoints,
|
||||
}: ZoneEditPaneProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
|
||||
@ -673,7 +675,9 @@ export default function ZoneEditPane({
|
||||
{t("masksAndZones.zones.speedEstimation.desc")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/zones#speed-estimation"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/zones#speed-estimation",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
37
web/src/hooks/use-doc-domain.ts
Normal file
37
web/src/hooks/use-doc-domain.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
/**
|
||||
* Hook to get documentation URLs based on current language
|
||||
*
|
||||
* @returns {Object} An object containing:
|
||||
* - getLocaleDocUrl: Function to get full documentation URL for a given path
|
||||
* - docDomain: Current documentation domain based on language
|
||||
*/
|
||||
export function useDocDomain() {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
// Map of language codes to their specific documentation domains
|
||||
const DOC_DOMAINS: Record<string, string> = {
|
||||
"zh-CN": "docs.frigate-cn.video",
|
||||
// Add other language-specific domains here as needed
|
||||
};
|
||||
|
||||
// Get the appropriate documentation domain for current language
|
||||
const docDomain = DOC_DOMAINS[i18n.language] || "docs.frigate.video";
|
||||
|
||||
/**
|
||||
* Get full documentation URL for a given path
|
||||
* @param {string} path - Documentation path (e.g. "/configuration/live")
|
||||
* @returns {string} Full documentation URL
|
||||
*/
|
||||
const getLocaleDocUrl = (path: string): string => {
|
||||
// Ensure path starts with a slash
|
||||
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
||||
return `https://${docDomain}${normalizedPath}`;
|
||||
};
|
||||
|
||||
return {
|
||||
getLocaleDocUrl,
|
||||
docDomain,
|
||||
};
|
||||
}
|
@ -22,6 +22,7 @@ import { Link } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import useSWR from "swr";
|
||||
import useSWRInfinite from "swr/infinite";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
const API_LIMIT = 25;
|
||||
|
||||
@ -29,6 +30,7 @@ export default function Explore() {
|
||||
// search field handler
|
||||
|
||||
const { t } = useTranslation(["views/explore"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
|
||||
const { data: config } = useSWR<FrigateConfig>("config", {
|
||||
revalidateOnFocus: false,
|
||||
@ -468,7 +470,7 @@ export default function Explore() {
|
||||
</div>
|
||||
<div className="flex items-center text-primary-variant">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/semantic_search"
|
||||
to={getLocaleDocUrl("configuration/semantic_search")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -119,6 +119,7 @@ import { toast } from "sonner";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type LiveCameraViewProps = {
|
||||
config?: FrigateConfig;
|
||||
@ -1017,6 +1018,7 @@ function FrigateCameraFeatures({
|
||||
cameraEnabled,
|
||||
}: FrigateCameraFeaturesProps) {
|
||||
const { t } = useTranslation(["views/live", "components/dialog"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
|
||||
const { payload: detectState, send: sendDetect } = useDetectState(
|
||||
camera.name,
|
||||
@ -1271,7 +1273,7 @@ function FrigateCameraFeatures({
|
||||
})}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
to={getLocaleDocUrl("configuration/live")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -1349,7 +1351,7 @@ function FrigateCameraFeatures({
|
||||
{t("stream.audio.tips.title")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
to={getLocaleDocUrl("configuration/live")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -1390,7 +1392,9 @@ function FrigateCameraFeatures({
|
||||
{t("stream.twoWayTalk.tips")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live/#webrtc-extra-configuration"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/live/#webrtc-extra-configuration",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -1597,7 +1601,7 @@ function FrigateCameraFeatures({
|
||||
})}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
to={getLocaleDocUrl("configuration/live")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -1668,7 +1672,7 @@ function FrigateCameraFeatures({
|
||||
{t("stream.audio.tips.title")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
to={getLocaleDocUrl("configuration/live")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -1709,7 +1713,9 @@ function FrigateCameraFeatures({
|
||||
{t("stream.twoWayTalk.tips")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live/#webrtc-extra-configuration"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/live/#webrtc-extra-configuration",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -31,6 +31,7 @@ import { Trans, useTranslation } from "react-i18next";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useAlertsState, useDetectionsState, useEnabledState } from "@/api/ws";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type CameraSettingsViewProps = {
|
||||
selectedCamera: string;
|
||||
@ -47,6 +48,7 @@ export default function CameraSettingsView({
|
||||
setUnsavedChanges,
|
||||
}: CameraSettingsViewProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
@ -352,7 +354,7 @@ export default function CameraSettingsView({
|
||||
</p>
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/review"
|
||||
to={getLocaleDocUrl("configuration/review")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type EnrichmentsSettings = {
|
||||
search: {
|
||||
@ -57,6 +58,7 @@ export default function EnrichmentsSettingsView({
|
||||
setUnsavedChanges,
|
||||
}: EnrichmentsSettingsViewProps) {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
const [changedValue, setChangedValue] = useState(false);
|
||||
@ -256,7 +258,7 @@ export default function EnrichmentsSettingsView({
|
||||
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/semantic_search"
|
||||
to={getLocaleDocUrl("configuration/semantic_search")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -402,7 +404,7 @@ export default function EnrichmentsSettingsView({
|
||||
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/face_recognition"
|
||||
to={getLocaleDocUrl("configuration/face_recognition")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -503,7 +505,9 @@ export default function EnrichmentsSettingsView({
|
||||
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/license_plate_recognition"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/license_plate_recognition",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -549,7 +553,9 @@ export default function EnrichmentsSettingsView({
|
||||
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/license_plate_recognition"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/license_plate_recognition",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from "@/components/ui/select";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type FrigatePlusModel = {
|
||||
id: string;
|
||||
@ -49,6 +50,7 @@ export default function FrigatePlusSettingsView({
|
||||
setUnsavedChanges,
|
||||
}: FrigateSettingsViewProps) {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
const [changedValue, setChangedValue] = useState(false);
|
||||
@ -451,7 +453,7 @@ export default function FrigatePlusSettingsView({
|
||||
</p>
|
||||
<div className="mt-2 flex items-center text-primary-variant">
|
||||
<Link
|
||||
to="https://docs.frigate.video/plus/faq"
|
||||
to={getLocaleDocUrl("plus/faq")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -41,6 +41,8 @@ import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
||||
import { useSearchEffect } from "@/hooks/use-overlay-state";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type MasksAndZoneViewProps = {
|
||||
selectedCamera: string;
|
||||
selectedZoneMask?: PolygonType[];
|
||||
@ -53,6 +55,7 @@ export default function MasksAndZonesView({
|
||||
setUnsavedChanges,
|
||||
}: MasksAndZoneViewProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const [allPolygons, setAllPolygons] = useState<Polygon[]>([]);
|
||||
const [editingPolygons, setEditingPolygons] = useState<Polygon[]>([]);
|
||||
@ -513,7 +516,7 @@ export default function MasksAndZonesView({
|
||||
<p>{t("masksAndZones.zones.desc.title")}</p>
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/zones"
|
||||
to={getLocaleDocUrl("configuration/zones")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -579,7 +582,9 @@ export default function MasksAndZonesView({
|
||||
<p>{t("masksAndZones.motionMasks.desc.title")}</p>
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/masks#motion-masks"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/masks#motion-masks",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -649,7 +654,9 @@ export default function MasksAndZonesView({
|
||||
<p>{t("masksAndZones.objectMasks.desc.title")}</p>
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/masks#object-filter-masks"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/masks#object-filter-masks",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -22,6 +22,7 @@ import { Link } from "react-router-dom";
|
||||
import { LuExternalLink } from "react-icons/lu";
|
||||
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type MotionTunerViewProps = {
|
||||
selectedCamera: string;
|
||||
@ -39,6 +40,7 @@ export default function MotionTunerView({
|
||||
setUnsavedChanges,
|
||||
}: MotionTunerViewProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
const { data: config, mutate: updateConfig } =
|
||||
useSWR<FrigateConfig>("config");
|
||||
const [changedValue, setChangedValue] = useState(false);
|
||||
@ -198,7 +200,7 @@ export default function MotionTunerView({
|
||||
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/motion_detection"
|
||||
to={getLocaleDocUrl("configuration/motion_detection")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -45,6 +45,7 @@ import FilterSwitch from "@/components/filter/FilterSwitch";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDateLocale } from "@/hooks/use-date-locale";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
const NOTIFICATION_SERVICE_WORKER = "notifications-worker.js";
|
||||
|
||||
@ -61,6 +62,7 @@ export default function NotificationView({
|
||||
setUnsavedChanges,
|
||||
}: NotificationsSettingsViewProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
|
||||
const { data: config, mutate: updateConfig } = useSWR<FrigateConfig>(
|
||||
"config",
|
||||
@ -316,7 +318,7 @@ export default function NotificationView({
|
||||
<p>{t("notification.notificationSettings.desc")}</p>
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/notifications"
|
||||
to={getLocaleDocUrl("configuration/notifications")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -338,7 +340,7 @@ export default function NotificationView({
|
||||
</Trans>
|
||||
<div className="mt-3 flex items-center">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/authentication"
|
||||
to={getLocaleDocUrl("configuration/authentication")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
@ -371,7 +373,7 @@ export default function NotificationView({
|
||||
<p>{t("notification.notificationSettings.desc")}</p>
|
||||
<div className="flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/notifications"
|
||||
to={getLocaleDocUrl("configuration/notifications")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
@ -28,6 +28,7 @@ import DebugDrawingLayer from "@/components/overlay/DebugDrawingLayer";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { isDesktop } from "react-device-detect";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
|
||||
type ObjectSettingsViewProps = {
|
||||
selectedCamera?: string;
|
||||
@ -42,6 +43,8 @@ export default function ObjectSettingsView({
|
||||
}: ObjectSettingsViewProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
|
||||
const { getLocaleDocUrl } = useDocDomain();
|
||||
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
@ -258,7 +261,9 @@ export default function ObjectSettingsView({
|
||||
{t("debug.objectShapeFilterDrawing.tips")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/object_filters#object-shape"
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/object_filters#object-shape",
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
|
Loading…
Reference in New Issue
Block a user