Configure web display language from profile settings, default is set to
This commit is contained in:
parent
261f21af55
commit
2bc4fcc77f
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
|
|
@ -9,7 +9,8 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
|||
import { Save } from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useI18n } from '@/locales/client';
|
||||
import { useI18n, useCurrentLocale, useChangeLocale } from '@/locales/client';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
|
||||
const mockUserProfile = {
|
||||
name: 'Alice Wonderland',
|
||||
|
|
@ -23,6 +24,8 @@ const mockUserProfile = {
|
|||
export default function ProfilePage() {
|
||||
const { toast } = useToast();
|
||||
const t = useI18n();
|
||||
const currentLocale = useCurrentLocale();
|
||||
const changeLocale = useChangeLocale();
|
||||
|
||||
const [name, setName] = useState(mockUserProfile.name);
|
||||
const [email, setEmail] = useState(mockUserProfile.email);
|
||||
|
|
@ -33,39 +36,58 @@ export default function ProfilePage() {
|
|||
const [currentPassword, setCurrentPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [confirmNewPassword, setConfirmNewPassword] = useState('');
|
||||
|
||||
const [selectedLanguage, setSelectedLanguage] = useState(currentLocale);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isPasswordLoading, setIsPasswordLoading] = useState(false);
|
||||
const [isLanguageLoading, setIsLanguageLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedLanguage(currentLocale);
|
||||
}, [currentLocale]);
|
||||
|
||||
const handleProfileUpdate = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
console.log("Updating profile:", { name, avatarUrl, bio, phone, location });
|
||||
toast({ title: "Profile Updated", description: "Your profile information has been saved." }); // Translate
|
||||
toast({ title: t('dashboard.profile.save_button'), description: "Your profile information has been saved." });
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const handlePasswordChange = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (newPassword !== confirmNewPassword) {
|
||||
toast({ title: "Password Mismatch", description: "New passwords do not match.", variant: "destructive" }); // Translate
|
||||
toast({ title: "Password Mismatch", description: "New passwords do not match.", variant: "destructive" });
|
||||
return;
|
||||
}
|
||||
if (newPassword.length < 6) {
|
||||
toast({ title: "Password Too Short", description: "Password must be at least 6 characters.", variant: "destructive" }); // Translate
|
||||
toast({ title: "Password Too Short", description: "Password must be at least 6 characters.", variant: "destructive" });
|
||||
return;
|
||||
}
|
||||
setIsPasswordLoading(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
console.log("Changing password");
|
||||
toast({ title: "Password Updated", description: "Your password has been changed successfully." }); // Translate
|
||||
toast({ title: t('dashboard.profile.update_password_button'), description: "Your password has been changed successfully." });
|
||||
setCurrentPassword('');
|
||||
setNewPassword('');
|
||||
setConfirmNewPassword('');
|
||||
setIsPasswordLoading(false);
|
||||
};
|
||||
|
||||
const handleLanguageChange = (newLang: 'en' | 'zh-TW') => {
|
||||
setIsLanguageLoading(true);
|
||||
changeLocale(newLang);
|
||||
localStorage.setItem('userPreferredLanguage', newLang);
|
||||
setSelectedLanguage(newLang);
|
||||
toast({ title: t('dashboard.profile.language_settings_title'), description: t('dashboard.profile.language_updated_toast') });
|
||||
// No need to manually set isLanguageLoading to false if the page reloads/re-renders due to locale change
|
||||
// However, if changeLocale doesn't cause an immediate unmount, manage loading state appropriately.
|
||||
// For simplicity, we assume changeLocale will lead to a re-render where currentLocale updates.
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="space-y-8 max-w-3xl mx-auto">
|
||||
<div>
|
||||
|
|
@ -155,6 +177,32 @@ export default function ProfilePage() {
|
|||
</CardFooter>
|
||||
</form>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-lg">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl font-headline">{t('dashboard.profile.language_settings_title')}</CardTitle>
|
||||
<CardDescription>{t('dashboard.profile.language_settings_description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="language-select">{t('dashboard.profile.display_language_label')}</Label>
|
||||
<Select
|
||||
value={selectedLanguage}
|
||||
onValueChange={(value) => handleLanguageChange(value as 'en' | 'zh-TW')}
|
||||
disabled={isLanguageLoading}
|
||||
>
|
||||
<SelectTrigger id="language-select">
|
||||
<SelectValue placeholder={t('lang.select_language')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="en">{t('lang.english')}</SelectItem>
|
||||
<SelectItem value="zh-TW">{t('lang.traditional_chinese')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,23 +15,41 @@ import {
|
|||
import { useState, useEffect } from 'react';
|
||||
import { ThemeToggleButton } from '@/components/ui/theme-toggle';
|
||||
import LanguageSwitcher from './LanguageSwitcher';
|
||||
import { useI18n, useCurrentLocale } from '@/locales/client';
|
||||
import { useI18n, useCurrentLocale, useChangeLocale } from '@/locales/client';
|
||||
|
||||
export default function Header() {
|
||||
const pathname = usePathname();
|
||||
const t = useI18n();
|
||||
const locale = useCurrentLocale();
|
||||
const changeLocale = useChangeLocale();
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
const authStatus = localStorage.getItem('isToyShareAuthenticated');
|
||||
setIsAuthenticated(authStatus === 'true');
|
||||
}, [pathname]); // Listen to pathname changes to re-check auth if needed after navigation
|
||||
}, []); // Run only once on mount
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted) { // Ensure this runs only on the client after mount
|
||||
const preferredLang = localStorage.getItem('userPreferredLanguage') as 'en' | 'zh-TW' | null;
|
||||
if (preferredLang && preferredLang !== locale) {
|
||||
// Check if the current pathname without locale prefix exists for the preferredLang
|
||||
// This avoids redirect loops if a page doesn't exist in the preferredLang
|
||||
const currentPathWithoutLocale = pathname.startsWith(`/${locale}`) ? pathname.substring(`/${locale}`.length) : pathname;
|
||||
// It's hard to verify if `currentPathWithoutLocale` is valid for `preferredLang` without a full route list
|
||||
// For simplicity, we'll directly change locale. If a 404 occurs, user can switch back.
|
||||
changeLocale(preferredLang);
|
||||
}
|
||||
}
|
||||
}, [isMounted, locale, pathname, changeLocale]);
|
||||
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('isToyShareAuthenticated');
|
||||
setIsAuthenticated(false);
|
||||
// No need to router.push here, links will use current locale
|
||||
// No need to router.push here, links will use current locale, or redirect logic will apply
|
||||
};
|
||||
|
||||
// Helper to remove locale prefix for path comparison
|
||||
|
|
@ -78,7 +96,7 @@ export default function Header() {
|
|||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={handleLogout}>
|
||||
<LogIn className="mr-2 h-4 w-4" /> {/* Using LogIn icon for logout action for consistency */}
|
||||
<LogIn className="mr-2 h-4 w-4" />
|
||||
{t('header.logout')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
|
@ -102,7 +120,6 @@ export default function Header() {
|
|||
</nav>
|
||||
<LanguageSwitcher />
|
||||
<ThemeToggleButton />
|
||||
{/* Mobile menu trigger could be added here if needed */}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -92,8 +92,12 @@ export default {
|
|||
'dashboard.profile.confirm_new_password_label': 'Confirm New Password',
|
||||
'dashboard.profile.update_password_button': 'Update Password',
|
||||
'dashboard.profile.updating_password_button': 'Updating Password...',
|
||||
'dashboard.profile.language_settings_title': 'Language Settings',
|
||||
'dashboard.profile.language_settings_description': 'Choose your preferred display language for the application.',
|
||||
'dashboard.profile.display_language_label': 'Display Language',
|
||||
'dashboard.profile.language_updated_toast': 'Language updated successfully.',
|
||||
'lang.english': 'English',
|
||||
'lang.traditional_chinese': '繁體中文',
|
||||
'lang.traditional_chinese': 'Traditional Chinese',
|
||||
'lang.select_language': 'Select Language',
|
||||
'general.back_to_my_toys': 'Back to My Toys',
|
||||
'general.toy_not_found': 'Toy Not Found',
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ export default {
|
|||
'dashboard.profile.confirm_new_password_label': '確認新密碼',
|
||||
'dashboard.profile.update_password_button': '更新密碼',
|
||||
'dashboard.profile.updating_password_button': '更新密碼中...',
|
||||
'dashboard.profile.language_settings_title': '語言設定',
|
||||
'dashboard.profile.language_settings_description': '選擇您偏好的應用程式顯示語言。',
|
||||
'dashboard.profile.display_language_label': '顯示語言',
|
||||
'dashboard.profile.language_updated_toast': '語言已成功更新。',
|
||||
'lang.english': 'English',
|
||||
'lang.traditional_chinese': '繁體中文',
|
||||
'lang.select_language': '選擇語言',
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import { createI18nMiddleware } from 'next-international/middleware';
|
|||
|
||||
const I18nMiddleware = createI18nMiddleware({
|
||||
locales: ['en', 'zh-TW'],
|
||||
defaultLocale: 'en',
|
||||
urlMappingStrategy: 'rewrite', // Or 'redirect', depending on preference
|
||||
// prefix: 'as-needed' // default is 'as-needed'
|
||||
defaultLocale: 'zh-TW', // Changed default locale
|
||||
urlMappingStrategy: 'rewrite',
|
||||
});
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
|
|
@ -13,6 +12,5 @@ export function middleware(request: NextRequest) {
|
|||
}
|
||||
|
||||
export const config = {
|
||||
// Matcher ignoring `/_next/` and `/api/`
|
||||
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue