mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-06 14:52:47 +03: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:
co-authored by
Josh Hawkins
parent
db541abed4
commit
d34533981f
@@ -13,6 +13,7 @@ import { toast } from "sonner";
|
||||
import DeleteUserDialog from "@/components/overlay/DeleteUserDialog";
|
||||
import { HiTrash } from "react-icons/hi";
|
||||
import { FaUserEdit } from "react-icons/fa";
|
||||
|
||||
import { LuPlus, LuShield, LuUserCog } from "react-icons/lu";
|
||||
import {
|
||||
Table,
|
||||
@@ -30,8 +31,10 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import RoleChangeDialog from "@/components/overlay/RoleChangeDialog";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function AuthenticationView() {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const { data: users, mutate: mutateUsers } = useSWR<User[]>("users");
|
||||
|
||||
@@ -46,30 +49,38 @@ export default function AuthenticationView() {
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = "Authentication Settings - Frigate";
|
||||
}, []);
|
||||
document.title = t("documentTitle.authentication");
|
||||
}, [t]);
|
||||
|
||||
const onSavePassword = useCallback((user: string, password: string) => {
|
||||
axios
|
||||
.put(`users/${user}/password`, { password })
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
setShowSetPassword(false);
|
||||
toast.success("Password updated successfully", {
|
||||
position: "top-center",
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorMessage =
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.detail ||
|
||||
"Unknown error";
|
||||
toast.error(`Failed to save password: ${errorMessage}`, {
|
||||
position: "top-center",
|
||||
const onSavePassword = useCallback(
|
||||
(user: string, password: string) => {
|
||||
axios
|
||||
.put(`users/${user}/password`, { password })
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
setShowSetPassword(false);
|
||||
toast.success(t("users.toast.success.updatePassword"), {
|
||||
position: "top-center",
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorMessage =
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.detail ||
|
||||
"Unknown error";
|
||||
toast.error(
|
||||
t("users.toast.error.setPasswordFailed", {
|
||||
errorMessage,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
},
|
||||
[t],
|
||||
);
|
||||
|
||||
const onCreate = (
|
||||
user: string,
|
||||
@@ -85,7 +96,7 @@ export default function AuthenticationView() {
|
||||
users?.push({ username: user, role: role });
|
||||
return users;
|
||||
}, false);
|
||||
toast.success(`User ${user} created successfully`, {
|
||||
toast.success(t("users.toast.success.createUser", { user }), {
|
||||
position: "top-center",
|
||||
});
|
||||
}
|
||||
@@ -95,9 +106,14 @@ export default function AuthenticationView() {
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.detail ||
|
||||
"Unknown error";
|
||||
toast.error(`Failed to create user: ${errorMessage}`, {
|
||||
position: "top-center",
|
||||
});
|
||||
toast.error(
|
||||
t("users.toast.error.createUserFailed", {
|
||||
errorMessage,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -111,7 +127,7 @@ export default function AuthenticationView() {
|
||||
(users) => users?.filter((u) => u.username !== user),
|
||||
false,
|
||||
);
|
||||
toast.success(`User ${user} deleted successfully`, {
|
||||
toast.success(t("users.toast.success.deleteUser", { user }), {
|
||||
position: "top-center",
|
||||
});
|
||||
}
|
||||
@@ -121,9 +137,14 @@ export default function AuthenticationView() {
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.detail ||
|
||||
"Unknown error";
|
||||
toast.error(`Failed to delete user: ${errorMessage}`, {
|
||||
position: "top-center",
|
||||
});
|
||||
toast.error(
|
||||
t("users.toast.error.deleteUserFailed", {
|
||||
errorMessage,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -142,7 +163,7 @@ export default function AuthenticationView() {
|
||||
),
|
||||
false,
|
||||
);
|
||||
toast.success(`Role updated for ${user}`, {
|
||||
toast.success(t("users.toast.success.roleUpdated", { user }), {
|
||||
position: "top-center",
|
||||
});
|
||||
}
|
||||
@@ -152,9 +173,14 @@ export default function AuthenticationView() {
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.detail ||
|
||||
"Unknown error";
|
||||
toast.error(`Failed to update role: ${errorMessage}`, {
|
||||
position: "top-center",
|
||||
});
|
||||
toast.error(
|
||||
t("users.toast.error.roleUpdateFailed", {
|
||||
errorMessage,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -173,20 +199,20 @@ export default function AuthenticationView() {
|
||||
<div className="mb-5 flex flex-row items-center justify-between gap-2">
|
||||
<div className="flex flex-col items-start">
|
||||
<Heading as="h3" className="my-2">
|
||||
User Management
|
||||
{t("users.management")}
|
||||
</Heading>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Manage this Frigate instance's user accounts.
|
||||
{t("users.management.desc")}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
className="flex items-center gap-2 self-start sm:self-auto"
|
||||
aria-label="Add a new user"
|
||||
aria-label={t("users.addUser")}
|
||||
variant="default"
|
||||
onClick={() => setShowCreate(true)}
|
||||
>
|
||||
<LuPlus className="size-4" />
|
||||
Add User
|
||||
{t("users.addUser")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-6 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
@@ -195,16 +221,20 @@ export default function AuthenticationView() {
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-muted/50">
|
||||
<TableRow>
|
||||
<TableHead className="w-[250px]">Username</TableHead>
|
||||
<TableHead>Role</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
<TableHead className="w-[250px]">
|
||||
{t("users.table.username")}
|
||||
</TableHead>
|
||||
<TableHead>{t("users.table.role")}</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("users.table.actions")}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{users.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="h-24 text-center">
|
||||
No users found.
|
||||
{t("users.table.noUsers")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
@@ -231,7 +261,9 @@ export default function AuthenticationView() {
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{user.role || "viewer"}
|
||||
{t("role." + (user.role || "viewer"), {
|
||||
ns: "common",
|
||||
})}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
@@ -255,12 +287,12 @@ export default function AuthenticationView() {
|
||||
>
|
||||
<LuUserCog className="size-3.5" />
|
||||
<span className="ml-1.5 hidden sm:inline-block">
|
||||
Role
|
||||
{t("role.title", { ns: "common" })}
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Change user role</p>
|
||||
<p>{t("users.table.changeRole")}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
@@ -278,12 +310,12 @@ export default function AuthenticationView() {
|
||||
>
|
||||
<FaUserEdit className="size-3.5" />
|
||||
<span className="ml-1.5 hidden sm:inline-block">
|
||||
Password
|
||||
{t("users.table.password")}
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Update password</p>
|
||||
<p>{t("users.updatePassword")}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
@@ -301,12 +333,12 @@ export default function AuthenticationView() {
|
||||
>
|
||||
<HiTrash className="size-3.5" />
|
||||
<span className="ml-1.5 hidden sm:inline-block">
|
||||
Delete
|
||||
{t("button.delete", { ns: "common" })}
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Delete user</p>
|
||||
<p>{t("users.table.deleteUser")}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user