Fix PR feedback: Improve Profile section translations and UX

- Fixed password reset translations in Profile section
- Moved language selector from TopNavbar to Profile page
- Added profile.selectPreferredLanguage translation key
- Improved user experience for language preferences
This commit is contained in:
ZacharyZcR
2025-09-03 07:26:43 +08:00
parent 22779a3d03
commit 853d282d2f
5 changed files with 36 additions and 27 deletions

View File

@@ -609,7 +609,8 @@
"user": "User",
"authMethod": "Authentication Method",
"local": "Local",
"external": "External (OIDC)"
"external": "External (OIDC)",
"selectPreferredLanguage": "Select your preferred language for the interface"
},
"placeholders": {
"enterCode": "000000",

View File

@@ -609,7 +609,8 @@
"user": "用户",
"authMethod": "认证方式",
"local": "本地",
"external": "外部 (OIDC)"
"external": "外部 (OIDC)",
"selectPreferredLanguage": "选择您的界面首选语言"
},
"placeholders": {
"enterCode": "000000",

View File

@@ -13,7 +13,6 @@ import {
import {Input} from "@/components/ui/input.tsx";
import {Checkbox} from "@/components/ui/checkbox.tsx";
import {Separator} from "@/components/ui/separator.tsx";
import {LanguageSwitcher} from "@/components/LanguageSwitcher";
import {useTranslation} from "react-i18next";
interface TopNavbarProps {
@@ -267,8 +266,6 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
</div>
<div className="flex items-center justify-center gap-2 flex-1 px-2">
<LanguageSwitcher />
<Button
variant="outline"
className="w-[30px] h-[30px]"

View File

@@ -6,6 +6,7 @@ import {Label} from "@/components/ui/label.tsx";
import {Input} from "@/components/ui/input.tsx";
import {Button} from "@/components/ui/button.tsx";
import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert.tsx";
import {useTranslation} from "react-i18next";
interface PasswordResetProps {
userInfo: {
@@ -17,6 +18,7 @@ interface PasswordResetProps {
}
export function PasswordReset({userInfo}: PasswordResetProps) {
const {t} = useTranslation();
const [error, setError] = useState<string | null>(null);
const [resetStep, setResetStep] = useState<"initiate" | "verify" | "newPassword">("initiate");
@@ -35,7 +37,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
setResetStep("verify");
setError(null);
} catch (err: any) {
setError(err?.response?.data?.error || err?.message || "Failed to initiate password reset");
setError(err?.response?.data?.error || err?.message || t('errors.failedPasswordReset'));
} finally {
setResetLoading(false);
}
@@ -60,7 +62,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
setResetStep("newPassword");
setError(null);
} catch (err: any) {
setError(err?.response?.data?.error || "Failed to verify reset code");
setError(err?.response?.data?.error || t('errors.failedVerifyCode'));
} finally {
setResetLoading(false);
}
@@ -71,13 +73,13 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
setResetLoading(true);
if (newPassword !== confirmPassword) {
setError("Passwords do not match");
setError(t('errors.passwordMismatch'));
setResetLoading(false);
return;
}
if (newPassword.length < 6) {
setError("Password must be at least 6 characters long");
setError(t('errors.weakPassword'));
setResetLoading(false);
return;
}
@@ -94,7 +96,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
setResetSuccess(true);
} catch (err: any) {
setError(err?.response?.data?.error || "Failed to complete password reset");
setError(err?.response?.data?.error || t('errors.failedCompleteReset'));
} finally {
setResetLoading(false);
}
@@ -115,7 +117,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
Password
</CardTitle>
<CardDescription>
Change your account password
{t('profile.changePassword')}
</CardDescription>
</CardHeader>
<CardContent>
@@ -129,7 +131,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
disabled={resetLoading || !userInfo.username.trim()}
onClick={handleInitiatePasswordReset}
>
{resetLoading ? Spinner : "Send Reset Code"}
{resetLoading ? Spinner : t('auth.sendResetCode')}
</Button>
</div>
</>
@@ -138,12 +140,11 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
{resetStep === "verify" && (
<>
<div className="text-center text-muted-foreground mb-4">
<p>Enter the 6-digit code from the docker container logs for
user: <strong>{userInfo.username}</strong></p>
<p>{t('auth.enterResetCode')}: <strong>{userInfo.username}</strong></p>
</div>
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<Label htmlFor="reset-code">Reset Code</Label>
<Label htmlFor="reset-code">{t('auth.resetCode')}</Label>
<Input
id="reset-code"
type="text"
@@ -162,7 +163,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
disabled={resetLoading || resetCode.length !== 6}
onClick={handleVerifyResetCode}
>
{resetLoading ? Spinner : "Verify Code"}
{resetLoading ? Spinner : t('auth.verifyCode')}
</Button>
<Button
type="button"
@@ -174,7 +175,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
setResetCode("");
}}
>
Back
{t('common.back')}
</Button>
</div>
</>
@@ -183,10 +184,9 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
{resetSuccess && (
<>
<Alert className="">
<AlertTitle>Success!</AlertTitle>
<AlertTitle>{t('auth.passwordResetSuccess')}</AlertTitle>
<AlertDescription>
Your password has been successfully reset! You can now log in
with your new password.
{t('auth.passwordResetSuccessDesc')}
</AlertDescription>
</Alert>
</>
@@ -195,12 +195,11 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
{resetStep === "newPassword" && !resetSuccess && (
<>
<div className="text-center text-muted-foreground mb-4">
<p>Enter your new password for
user: <strong>{userInfo.username}</strong></p>
<p>{t('auth.enterNewPassword')}: <strong>{userInfo.username}</strong></p>
</div>
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<Label htmlFor="new-password">New Password</Label>
<Label htmlFor="new-password">{t('auth.newPassword')}</Label>
<Input
id="new-password"
type="password"
@@ -213,7 +212,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="confirm-password">Confirm Password</Label>
<Label htmlFor="confirm-password">{t('auth.confirmNewPassword')}</Label>
<Input
id="confirm-password"
type="password"
@@ -231,7 +230,7 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
disabled={resetLoading || !newPassword || !confirmPassword}
onClick={handleCompletePasswordReset}
>
{resetLoading ? Spinner : "Reset Password"}
{resetLoading ? Spinner : t('auth.resetPassword')}
</Button>
<Button
type="button"
@@ -244,14 +243,14 @@ export function PasswordReset({userInfo}: PasswordResetProps) {
setConfirmPassword("");
}}
>
Back
{t('common.back')}
</Button>
</div>
</>
)}
{error && (
<Alert variant="destructive" className="mt-4">
<AlertTitle>Error</AlertTitle>
<AlertTitle>{t('common.error')}</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
)}

View File

@@ -11,6 +11,7 @@ import {getUserInfo} from "@/ui/main-axios.ts";
import {toast} from "sonner";
import {PasswordReset} from "@/ui/User/PasswordReset.tsx";
import {useTranslation} from "react-i18next";
import {LanguageSwitcher} from "@/components/LanguageSwitcher";
interface UserProfileProps {
isTopbarOpen?: boolean;
@@ -146,6 +147,16 @@ export function UserProfile({isTopbarOpen = true}: UserProfileProps) {
</p>
</div>
</div>
<div className="mt-6 pt-6 border-t">
<div className="flex items-center justify-between">
<div>
<Label>{t('common.language')}</Label>
<p className="text-sm text-muted-foreground mt-1">{t('profile.selectPreferredLanguage')}</p>
</div>
<LanguageSwitcher />
</div>
</div>
</CardContent>
</Card>
</TabsContent>