mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-19 23:08:08 +02:00
i18n workflow improvements and tweaks (#22586)
* mobile button spacing * prevent console warning about div being descendant of p * ensure consistent spacing * add missing i18n keys * i18n fixes - add missing translations - fix dot notation keys * use plain string * add missing key * add i18next-cli commands for extraction and status also add false positives removal for several keys * add i18n key check step to PR workflow * formatting
This commit is contained in:
@@ -60,7 +60,7 @@ export default function DeleteRoleDialog({
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p>
|
||||
<Trans
|
||||
ns={"views/settings"}
|
||||
ns="views/settings"
|
||||
values={{ role }}
|
||||
components={{ strong: <span className="font-medium" /> }}
|
||||
>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function DeleteTriggerDialog({
|
||||
<DialogTitle>{t("triggers.dialog.deleteTrigger.title")}</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans
|
||||
ns={"views/settings"}
|
||||
ns="views/settings"
|
||||
values={{ triggerName }}
|
||||
components={{ strong: <span className="font-medium" /> }}
|
||||
>
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function EditRoleCamerasDialog({
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans
|
||||
ns={"views/settings"}
|
||||
ns="views/settings"
|
||||
values={{ role }}
|
||||
components={{ strong: <span className="font-medium" /> }}
|
||||
>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function MobileTimelineDrawer({
|
||||
setDrawer(false);
|
||||
}}
|
||||
>
|
||||
{t("timeline")}
|
||||
{t("timeline.label")}
|
||||
</div>
|
||||
<div
|
||||
className={`mx-4 w-full py-2 text-center smart-capitalize ${selected == "events" ? "rounded-lg bg-secondary" : ""}`}
|
||||
|
||||
@@ -494,7 +494,7 @@ export default function CameraEditForm({
|
||||
<CardContent className="space-y-4 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="font-medium">
|
||||
{t("cameraWizard.step2.streamTitle", {
|
||||
{t("cameraWizard.step3.streamTitle", {
|
||||
number: index + 1,
|
||||
})}
|
||||
</h4>
|
||||
|
||||
@@ -338,8 +338,8 @@ export default function CameraWizardDialog({
|
||||
}
|
||||
} else {
|
||||
toast.success(
|
||||
t("camera.cameraConfig.toast.success", {
|
||||
cameraName: wizardData.cameraName,
|
||||
t("cameraWizard.save.success", {
|
||||
cameraName: friendlyName || finalCameraName,
|
||||
}),
|
||||
{ position: "top-center" },
|
||||
);
|
||||
|
||||
@@ -785,7 +785,7 @@ export default function ZoneEditPane({
|
||||
</div>
|
||||
<FormDescription>
|
||||
{t("masksAndZones.zones.speedEstimation.desc")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<span className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to={getLocaleDocUrl(
|
||||
"configuration/zones#speed-estimation",
|
||||
@@ -797,7 +797,7 @@ export default function ZoneEditPane({
|
||||
{t("readTheDocumentation", { ns: "common" })}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</span>
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -220,7 +220,7 @@ function Exports() {
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{t("deleteExport")}</AlertDialogTitle>
|
||||
<AlertDialogTitle>{t("deleteExport.label")}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t("deleteExport.desc", { exportName: deleteClip?.exportName })}
|
||||
</AlertDialogDescription>
|
||||
|
||||
@@ -81,7 +81,7 @@ function Live() {
|
||||
camera: `${cameraGroup[0].toUpperCase()}${cameraGroup.substring(1)}`,
|
||||
});
|
||||
} else {
|
||||
document.title = t("documentTitle", { ns: "views/live" });
|
||||
document.title = t("documentTitle.default", { ns: "views/live" });
|
||||
}
|
||||
}, [cameraGroup, selectedCameraName, t]);
|
||||
|
||||
|
||||
@@ -1397,10 +1397,12 @@ export default function Settings() {
|
||||
: "bg-selected";
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between pr-4 md:pr-0">
|
||||
<div>{t("menu." + key)}</div>
|
||||
<div className="flex w-full min-w-0 items-center justify-between pr-4 md:pr-0">
|
||||
<div className="min-w-0 flex-1 whitespace-normal break-words">
|
||||
{t("menu." + key)}
|
||||
</div>
|
||||
{(showOverrideDot || showUnsavedDot) && (
|
||||
<div className="ml-2 flex items-center gap-2">
|
||||
<div className="ml-2 flex shrink-0 items-center gap-2">
|
||||
{showOverrideDot && (
|
||||
<span
|
||||
className={cn("inline-block size-2 rounded-full", dotColor)}
|
||||
@@ -1747,7 +1749,7 @@ export default function Settings() {
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
className="ml-0"
|
||||
className="ml-0 h-auto min-h-8 py-1.5"
|
||||
isActive={pageToggle === filteredItems[0].key}
|
||||
onClick={() => {
|
||||
if (
|
||||
@@ -1788,6 +1790,7 @@ export default function Settings() {
|
||||
{filteredItems.map((item) => (
|
||||
<SidebarMenuSubItem key={item.key}>
|
||||
<SidebarMenuSubButton
|
||||
className="h-auto w-full py-1.5"
|
||||
isActive={pageToggle === item.key}
|
||||
onClick={() => {
|
||||
if (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { TFunction } from "i18next";
|
||||
|
||||
export const calculatePasswordStrength = (password: string): number => {
|
||||
if (!password) return 0;
|
||||
|
||||
@@ -16,13 +18,18 @@ export const getPasswordRequirements = (password: string) => ({
|
||||
|
||||
export const getPasswordStrengthLabel = (
|
||||
password: string,
|
||||
t: (key: string) => string,
|
||||
t: TFunction,
|
||||
): string => {
|
||||
const strength = calculatePasswordStrength(password);
|
||||
|
||||
if (!password) return "";
|
||||
if (strength < 1) return t("users.dialog.form.password.strength.weak");
|
||||
return t("users.dialog.form.password.strength.veryStrong");
|
||||
if (strength < 1)
|
||||
return t("users.dialog.form.password.strength.weak", {
|
||||
ns: "views/settings",
|
||||
});
|
||||
return t("users.dialog.form.password.strength.veryStrong", {
|
||||
ns: "views/settings",
|
||||
});
|
||||
};
|
||||
|
||||
export const getPasswordStrengthColor = (password: string): string => {
|
||||
|
||||
@@ -700,7 +700,7 @@ export function RecordingView({
|
||||
value="timeline"
|
||||
aria-label={t("timeline.aria")}
|
||||
>
|
||||
<div className="">{t("timeline")}</div>
|
||||
<div className="">{t("timeline.label")}</div>
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem
|
||||
className={`${timelineType == "events" ? "" : "text-muted-foreground"}`}
|
||||
|
||||
@@ -618,7 +618,7 @@ export default function ProfilesView({
|
||||
ns: "views/settings",
|
||||
})}
|
||||
/>
|
||||
<DialogFooter>
|
||||
<DialogFooter className="gap-2 md:gap-0">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
|
||||
Reference in New Issue
Block a user