mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-09-27 03:46:15 +08:00
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:
@@ -31,6 +31,7 @@ import useSWR from "swr";
|
||||
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";
|
||||
|
||||
type CameraStreamingDialogProps = {
|
||||
camera: string;
|
||||
@@ -49,6 +50,7 @@ export function CameraStreamingDialog({
|
||||
setIsDialogOpen,
|
||||
onSave,
|
||||
}: CameraStreamingDialogProps) {
|
||||
const { t } = useTranslation(["components/camera"]);
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -167,30 +169,36 @@ export function CameraStreamingDialog({
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader className="mb-4">
|
||||
<DialogTitle className="capitalize">
|
||||
{camera.replaceAll("_", " ")} Streaming Settings
|
||||
{t("group.camera.setting.title", {
|
||||
cameraName: camera.replaceAll("_", " "),
|
||||
})}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Change the live streaming options for this camera group's dashboard.{" "}
|
||||
<em>These settings are device/browser-specific.</em>
|
||||
<Trans ns="components/camera">group.camera.setting.desc</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col space-y-8">
|
||||
{!isRestreamed && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label>Stream</Label>
|
||||
<Label></Label>
|
||||
<div className="flex flex-row items-center gap-1 text-sm text-muted-foreground">
|
||||
<LuX className="size-4 text-danger" />
|
||||
<div>Restreaming is not enabled for this camera.</div>
|
||||
<div>
|
||||
{t("streaming.restreaming.disabled", {
|
||||
ns: "components/dialog",
|
||||
})}
|
||||
</div>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="cursor-pointer p-0">
|
||||
<LuInfo className="size-4" />
|
||||
<span className="sr-only">Info</span>
|
||||
<span className="sr-only">
|
||||
{t("button.info", { ns: "common" })}
|
||||
</span>
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80 text-xs">
|
||||
Set up go2rtc for additional live view options and audio for
|
||||
this camera.
|
||||
{t("streaming.restreaming.desc", { ns: "components/dialog" })}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
@@ -198,7 +206,9 @@ export function CameraStreamingDialog({
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
Read the documentation{" "}
|
||||
{t("streaming.restreaming.readTheDocumentation", {
|
||||
ns: "components/dialog",
|
||||
})}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
@@ -231,22 +241,23 @@ export function CameraStreamingDialog({
|
||||
{supportsAudioOutput ? (
|
||||
<>
|
||||
<LuCheck className="size-4 text-success" />
|
||||
<div>Audio is available for this stream</div>
|
||||
<div>{t("group.camera.setting.audioIsAvailable")}</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LuX className="size-4 text-danger" />
|
||||
<div>Audio is unavailable for this stream</div>
|
||||
<div>{t("group.camera.setting.audioIsUnavailable")}</div>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="cursor-pointer p-0">
|
||||
<LuInfo className="size-4" />
|
||||
<span className="sr-only">Info</span>
|
||||
<span className="sr-only">
|
||||
{t("button.info", { ns: "common" })}
|
||||
</span>
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80 text-xs">
|
||||
Audio must be output from your camera and configured
|
||||
in go2rtc for this stream.
|
||||
{t("group.camera.setting.audio.desc")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to="https://docs.frigate.video/configuration/live"
|
||||
@@ -254,7 +265,7 @@ export function CameraStreamingDialog({
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
Read the documentation{" "}
|
||||
{t("group.camera.setting.audio.desc.document")}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
@@ -268,7 +279,7 @@ export function CameraStreamingDialog({
|
||||
)}
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<Label htmlFor="streaming-method" className="text-right">
|
||||
Streaming Method
|
||||
{t("group.camera.setting.streamMethod.label")}
|
||||
</Label>
|
||||
<Select
|
||||
value={streamType}
|
||||
@@ -278,38 +289,44 @@ export function CameraStreamingDialog({
|
||||
<SelectValue placeholder="Choose a streaming option" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="no-streaming">No Streaming</SelectItem>
|
||||
<SelectItem value="smart">
|
||||
Smart Streaming (recommended)
|
||||
<SelectItem value="no-streaming">
|
||||
{t("group.camera.setting.streamMethod.method.noStreaming")}
|
||||
</SelectItem>
|
||||
<SelectItem value="smart">
|
||||
{t("group.camera.setting.streamMethod.method.smartStreaming")}
|
||||
</SelectItem>
|
||||
<SelectItem value="continuous">
|
||||
{t(
|
||||
"group.camera.setting.streamMethod.method.continuousStreaming",
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="continuous">Continuous Streaming</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{streamType === "no-streaming" && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Camera images will only update once per minute and no live
|
||||
streaming will occur.
|
||||
{t("group.camera.setting.streamMethod.method.noStreaming.desc")}
|
||||
</p>
|
||||
)}
|
||||
{streamType === "smart" && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Smart streaming will update your camera image once per minute when
|
||||
no detectable activity is occurring to conserve bandwidth and
|
||||
resources. When activity is detected, the image seamlessly
|
||||
switches to a live stream.
|
||||
{t(
|
||||
"group.camera.setting.streamMethod.method.smartStreaming.desc",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{streamType === "continuous" && (
|
||||
<>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Camera image will always be a live stream when visible on the
|
||||
dashboard, even if no activity is being detected.
|
||||
{t(
|
||||
"group.camera.setting.streamMethod.method.continuousStreaming.desc",
|
||||
)}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<IoIosWarning className="mr-2 size-5 text-danger" />
|
||||
<div className="max-w-[85%] text-sm">
|
||||
Continuous streaming may cause high bandwidth usage and
|
||||
performance issues. Use with caution.
|
||||
{t(
|
||||
"group.camera.setting.streamMethod.method.continuousStreaming.desc.warning",
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -327,14 +344,12 @@ export function CameraStreamingDialog({
|
||||
htmlFor="compatibility"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Compatibility mode
|
||||
{t("group.camera.setting.compatibilityMode")}
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 leading-none">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enable this option only if your camera's live stream is displaying
|
||||
color artifacts and has a diagonal line on the right side of the
|
||||
image.
|
||||
{t("group.camera.setting.compatibilityMode.desc")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -343,14 +358,14 @@ export function CameraStreamingDialog({
|
||||
<div className="flex flex-row gap-2 pt-5">
|
||||
<Button
|
||||
className="flex flex-1"
|
||||
aria-label="Cancel"
|
||||
aria-label={t("button.cancel", { ns: "common" })}
|
||||
onClick={handleCancel}
|
||||
>
|
||||
Cancel
|
||||
{t("button.cancel", { ns: "common" })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="select"
|
||||
aria-label="Save"
|
||||
aria-label={t("button.save", { ns: "common" })}
|
||||
disabled={isLoading}
|
||||
className="flex flex-1"
|
||||
onClick={handleSave}
|
||||
@@ -358,10 +373,10 @@ export function CameraStreamingDialog({
|
||||
{isLoading ? (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<ActivityIndicator />
|
||||
<span>Saving...</span>
|
||||
<span>{t("button.saving", { ns: "common" })}</span>
|
||||
</div>
|
||||
) : (
|
||||
"Save"
|
||||
t("button.save", { ns: "common" })
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user