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:
GuoQing Liu
2025-03-16 10:36:20 -05:00
committed by GitHub
co-authored by Josh Hawkins
parent db541abed4
commit d34533981f
150 changed files with 6809 additions and 1926 deletions
@@ -11,8 +11,10 @@ import {
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { Label } from "../ui/label";
import { LuCheck, LuX } from "react-icons/lu";
import { useTranslation } from "react-i18next";
type SetPasswordProps = {
show: boolean;
@@ -27,6 +29,7 @@ export default function SetPasswordDialog({
onCancel,
username,
}: SetPasswordProps) {
const { t } = useTranslation(["views/settings"]);
const [password, setPassword] = useState<string>("");
const [confirmPassword, setConfirmPassword] = useState<string>("");
const [passwordStrength, setPasswordStrength] = useState<number>(0);
@@ -77,10 +80,13 @@ export default function SetPasswordDialog({
const getStrengthLabel = () => {
if (!password) return "";
if (passwordStrength <= 1) return "Weak";
if (passwordStrength === 2) return "Medium";
if (passwordStrength === 3) return "Strong";
return "Very Strong";
if (passwordStrength <= 1)
return t("users.dialog.form.password.strength.weak");
if (passwordStrength === 2)
return t("users.dialog.form.password.strength.medium");
if (passwordStrength === 3)
return t("users.dialog.form.password.strength.strong");
return t("users.dialog.form.password.strength.veryStrong");
};
const getStrengthColor = () => {
@@ -96,16 +102,23 @@ export default function SetPasswordDialog({
<DialogContent className="sm:max-w-[425px]">
<DialogHeader className="space-y-2">
<DialogTitle>
{username ? `Update Password for ${username}` : "Set Password"}
{username
? t("users.dialog.passwordSetting.updatePassword", {
username,
ns: "views/settings",
})
: t("users.dialog.passwordSetting.setPassword")}
</DialogTitle>
<DialogDescription>
Create a strong password to secure this account.
{t("users.dialog.passwordSetting.desc")}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="password">New Password</Label>
<Label htmlFor="password">
{t("users.dialog.form.newPassword")}
</Label>
<Input
id="password"
className="h-10"
@@ -115,7 +128,7 @@ export default function SetPasswordDialog({
setPassword(event.target.value);
setError(null);
}}
placeholder="Enter new password"
placeholder={t("users.dialog.form.newPassword.placeholder")}
autoFocus
/>
@@ -129,7 +142,7 @@ export default function SetPasswordDialog({
/>
</div>
<p className="text-xs text-muted-foreground">
Password strength:{" "}
{t("users.dialog.form.password.strength")}
<span className="font-medium">{getStrengthLabel()}</span>
</p>
</div>
@@ -137,7 +150,9 @@ export default function SetPasswordDialog({
</div>
<div className="space-y-2">
<Label htmlFor="confirm-password">Confirm Password</Label>
<Label htmlFor="confirm-password">
{t("users.dialog.form.password.confirm")}
</Label>
<Input
id="confirm-password"
className="h-10"
@@ -147,7 +162,9 @@ export default function SetPasswordDialog({
setConfirmPassword(event.target.value);
setError(null);
}}
placeholder="Confirm new password"
placeholder={t(
"users.dialog.form.newPassword.confirm.placeholder",
)}
/>
{/* Password match indicator */}
@@ -156,12 +173,16 @@ export default function SetPasswordDialog({
{password === confirmPassword ? (
<>
<LuCheck className="size-3.5 text-green-500" />
<span className="text-green-600">Passwords match</span>
<span className="text-green-600">
{t("users.dialog.form.password.match")}
</span>
</>
) : (
<>
<LuX className="size-3.5 text-red-500" />
<span className="text-red-600">Passwords don't match</span>
<span className="text-red-600">
{t("users.dialog.form.password.notMatch")}
</span>
</>
)}
</div>
@@ -180,20 +201,20 @@ export default function SetPasswordDialog({
<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={onCancel}
type="button"
>
Cancel
{t("button.cancel", { ns: "common" })}
</Button>
<Button
variant="select"
aria-label="Save"
aria-label={t("button.save", { ns: "common" })}
className="flex flex-1"
onClick={handleSave}
disabled={!password || password !== confirmPassword}
>
Save
{t("button.save", { ns: "common" })}
</Button>
</div>
</div>