Переглянути джерело

perf: 完成页面中英文切换功能

kuns 4 тижнів тому
батько
коміт
5190b1f51c

+ 7 - 5
app/(auth)/(tabs)/_layout.tsx

@@ -2,11 +2,13 @@ import { useEffect, useState } from 'react';
 import { Tabs, useSegments } from 'expo-router';
 import { Dimensions, Platform } from 'react-native';
 import { TabAccountIcon, TabChargingIcon, TabHomeIcon } from '../../../component/global/SVG';
-
 import { useColorScheme } from 'nativewind';
+import { useTranslation } from '../../../util/hooks/useTranslation';
+
 export default function TabLayout() {
     const { height } = Dimensions.get('window');
     const { colorScheme } = useColorScheme();
+    const { t } = useTranslation();
 
     const activeColor = colorScheme === 'dark' ? '#36DFFF' : '#02677D';
     const inactiveColor = colorScheme === 'dark' ? '#666666' : '#BBBBBB';
@@ -52,7 +54,7 @@ export default function TabLayout() {
             <Tabs.Screen
                 name="(home)"
                 options={{
-                    title: '主頁',
+                    title: t('tabs.home'),
                     // tabBarHideOnKeyboard: true,
                     tabBarIcon: ({ focused }) => <TabHomeIcon color={focused ? activeColor : inactiveColor} />
                 }}
@@ -60,7 +62,7 @@ export default function TabLayout() {
             <Tabs.Screen
                 name="(charging)"
                 options={{
-                    title: '充電',
+                    title: t('tabs.charging'),
                     tabBarHideOnKeyboard: true,
                     tabBarIcon: ({ focused }) => <TabChargingIcon color={focused ? activeColor : inactiveColor} />
                 }}
@@ -68,11 +70,11 @@ export default function TabLayout() {
             <Tabs.Screen
                 name="(account)"
                 options={{
-                    title: '帳戶',
+                    title: t('tabs.account'),
                     tabBarHideOnKeyboard: true,
                     tabBarIcon: ({ focused }) => <TabAccountIcon color={focused ? activeColor : inactiveColor} />
                 }}
             />
         </Tabs>
     );
-}
+}

+ 16 - 15
component/accountPages/accountMainPageComponent.tsx

@@ -23,6 +23,7 @@ import { usePushNotifications } from '../../app/hooks/usePushNotifications';
 import useUserInfoStore from '../../providers/userinfo_store';
 import { authenticationService } from '../../service/authService';
 import { handleGoWhatsApp } from '../../util/index';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const AccountMainPageComponent = () => {
     const { user, logout } = useAuth();
@@ -32,9 +33,9 @@ const AccountMainPageComponent = () => {
     const aspectRatio = 210 / 375;
     const [notificationsEnabled, setNotificationsEnabled] = useState(false);
     const { expoPushToken, notification } = usePushNotifications();
-    // const notify_session_id = expoPushToken?.data || '';
-    // Check initial notification permission status
     const { notifySessionID } = useUserInfoStore();
+    const { t } = useTranslation();
+
     //everytime i am on this screen, i check if permission is granted.
     useFocusEffect(
         useCallback(() => {
@@ -54,7 +55,7 @@ const AccountMainPageComponent = () => {
                         if (res) {
                             console.log('Change notifySessionID Successfully!');
                         } else {
-                            Alert.alert('更新通知權限失敗', '請稍後再試');
+                            Alert.alert(t('account.notification_update_failed'), t('common.try_again_later'));
                         }
                     }
                     //TODO: if latestToken != notify_session_id, update notify_session_id to latestToken
@@ -74,13 +75,13 @@ const AccountMainPageComponent = () => {
     );
 
     const toggleNotifications = async () => {
-        Alert.alert('通知設定', '要更改通知設定,請前往手機的設定頁面,允許CrazyCharge通知。', [
+        Alert.alert(t('account.notification_settings'), t('account.notification_settings_message'), [
             {
-                text: '取消',
+                text: t('common.cancel'),
                 style: 'cancel'
             },
             {
-                text: '前往設定',
+                text: t('account.go_to_settings'),
                 onPress: () => Linking.openSettings()
             }
         ]);
@@ -101,7 +102,7 @@ const AccountMainPageComponent = () => {
                         onPress={() => router.push('/(account)/(wallet)/walletPage')}
                     >
                         <WalletSvg isDark={colorScheme == 'dark'} />
-                        <Text className="text-black dark:text-[#36DFFF]">錢包</Text>
+                        <Text className="text-black dark:text-[#36DFFF]">{t('account.wallet')}</Text>
                     </Pressable>
 
                     {/* <Pressable
@@ -109,7 +110,7 @@ const AccountMainPageComponent = () => {
                         onPress={() => router.replace('myVehiclePage')}
                     >
                         <MyCarSvg />
-                        <Text>我的車輛</Text>
+                        <Text>{t('account.my_vehicle')}</Text>
                     </Pressable> */}
                     <Pressable
                         className="h-[114px] w-[30%] bg-[#e7f2f8] dark:bg-[#253336] items-center justify-center rounded-xl"
@@ -124,7 +125,7 @@ const AccountMainPageComponent = () => {
                         onPress={() => router.push('/activityRecordPage')}
                     >
                         <ActivitySvg isDark={colorScheme == 'dark'} />
-                        <Text className="text-black dark:text-[#36DFFF]">充電記錄</Text>
+                        <Text className="text-black text-center dark:text-[#36DFFF]">{t('account.charging_history')}</Text>
                     </Pressable>
                 </View>
                 <View className="my-4">
@@ -150,7 +151,7 @@ const AccountMainPageComponent = () => {
                             }}
                         >
                             <AccountSettingIconSvg isDark={colorScheme == 'dark'} />
-                            <Text className="text-lg pl-2 text-black dark:text-white">帳戶管理</Text>
+                            <Text className="text-lg pl-2 text-black dark:text-white">{t('account.account_management')}</Text>
                         </Pressable>
                     </View>
 
@@ -185,7 +186,7 @@ const AccountMainPageComponent = () => {
                             <View className="flex flex-row justify-between w-full">
                                 <View className="flex flex-row items-center">
                                     <NotificationSvg isDark={colorScheme == 'dark'} />
-                                    <Text className="text-lg pl-2 text-black dark:text-white">允許通知</Text>
+                                    <Text className="text-lg pl-2 text-black dark:text-white">{t('account.allow_notifications')}</Text>
                                 </View>
                                 <Switch value={notificationsEnabled} onChange={toggleNotifications} />
                             </View>
@@ -206,7 +207,7 @@ const AccountMainPageComponent = () => {
                             <View className="flex flex-row justify-between w-full">
                                 <View className="flex flex-row items-center">
                                     <DarkModeSvg isDark={colorScheme == 'dark'} />
-                                    <Text className="text-lg pl-2 text-black dark:text-white">深色模式</Text>
+                                    <Text className="text-lg pl-2 text-black dark:text-white">{t('account.dark_mode')}</Text>
                                 </View>
                                 <Switch value={colorScheme == 'dark'} onChange={toggleColorScheme} />
                             </View>
@@ -225,7 +226,7 @@ const AccountMainPageComponent = () => {
                             }}
                         >
                             <UserTermsSvg />
-                            <Text className="text-lg pl-2 text-black dark:text-white">用戶條款</Text>
+                            <Text className="text-lg pl-2 text-black dark:text-white">{t('account.user_terms')}</Text>
                         </Pressable>
                     </View>
                     <View className="h-0.5  bg-[#f4f4f4] dark:bg-[#5E6C70]" />
@@ -241,7 +242,7 @@ const AccountMainPageComponent = () => {
                             }}
                         >
                             <SettingIconSvg isDark={colorScheme == 'dark'} />
-                            <Text className="text-lg pl-2 text-black dark:text-white">登出</Text>
+                            <Text className="text-lg pl-2 text-black dark:text-white">{t('account.logout')}</Text>
                         </Pressable>
                     </View>
                     <View className="h-0.5  bg-[#f4f4f4] dark:bg-[#5E6C70]" />
@@ -251,4 +252,4 @@ const AccountMainPageComponent = () => {
     );
 };
 
-export default AccountMainPageComponent;
+export default AccountMainPageComponent;

+ 39 - 31
component/accountPages/accountSettingPageComponent.tsx

@@ -4,34 +4,43 @@ import { SafeAreaView } from 'react-native-safe-area-context';
 import { router } from 'expo-router';
 import { CrossLogoSvg, RightArrowIconSvg } from '../global/SVG';
 import { AuthContext, useAuth } from '../../context/AuthProvider';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const AccountSettingPageComponent = () => {
     const { user } = useContext(AuthContext);
     const { logout } = useAuth();
-    const genderText = user?.gender === 'woman' ? '女' : user?.gender === 'man' ? '男' : user?.gender;
+    const { t } = useTranslation();
+    
+    const genderText = user?.gender === 'woman' ? t('accountSettings.female') : 
+                      user?.gender === 'man' ? t('accountSettings.male') : 
+                      user?.gender;
 
     const handleDeletionAccount = () => {
-        Alert.alert('Delete Account', 'Are you sure you want to delete your account? This action cannot be undone.', [
-            {
-                text: 'Cancel',
-                style: 'cancel'
-            },
-            {
-                text: 'Delete',
-                style: 'destructive',
-                onPress: async () => {
-                    try {
-                        logout();
-                        // await walletService.deleteWallet(user.id);
-                        // Handle successful deletion (e.g., logout and redirect)
-                        // You might want to add additional logic here
-                    } catch (error) {
-                        console.error('Error deleting account:', error);
-                        Alert.alert('Error', 'Failed to delete account. Please try again.');
+        Alert.alert(
+            t('accountSettings.delete_account'), 
+            t('accountSettings.delete_account_confirmation'), 
+            [
+                {
+                    text: t('common.cancel'),
+                    style: 'cancel'
+                },
+                {
+                    text: t('accountSettings.delete'),
+                    style: 'destructive',
+                    onPress: async () => {
+                        try {
+                            logout();
+                            // await walletService.deleteWallet(user.id);
+                            // Handle successful deletion (e.g., logout and redirect)
+                            // You might want to add additional logic here
+                        } catch (error) {
+                            console.error('Error deleting account:', error);
+                            Alert.alert(t('common.error'), t('accountSettings.delete_account_error'));
+                        }
                     }
                 }
-            }
-        ]);
+            ]
+        );
     };
 
     return (
@@ -45,14 +54,14 @@ const AccountSettingPageComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginVertical: 25 }}>帳戶管理</Text>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('accountSettings.account_management')}</Text>
                 </View>
                 <View>
                     <View className="flex-col">
                         <Pressable onPress={() => router.push('changeEmailPage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-col py-4">
-                                    <Text className="text-lg pb-1">電郵地址</Text>
+                                    <Text className="text-lg pb-1">{t('accountSettings.email')}</Text>
                                     <Text style={{ color: '#555555' }}>{user?.email}</Text>
                                 </View>
                                 <RightArrowIconSvg />
@@ -64,7 +73,7 @@ const AccountSettingPageComponent = () => {
                         <Pressable onPress={() => router.push('changePasswordPage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-col py-4">
-                                    <Text className="text-lg pb-1">帳戶密碼</Text>
+                                    <Text className="text-lg pb-1">{t('accountSettings.password')}</Text>
                                     <Text style={{ color: '#555555' }}>********</Text>
                                 </View>
                                 <RightArrowIconSvg />
@@ -76,7 +85,7 @@ const AccountSettingPageComponent = () => {
                         <Pressable onPress={() => router.push('changeNamePage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-col py-4">
-                                    <Text className="text-lg pb-1">暱稱</Text>
+                                    <Text className="text-lg pb-1">{t('accountSettings.nickname')}</Text>
                                     <Text style={{ color: '#555555' }}>{user?.nickname}</Text>
                                 </View>
                                 <RightArrowIconSvg />
@@ -88,7 +97,7 @@ const AccountSettingPageComponent = () => {
                         <Pressable onPress={() => router.push('changeGenderPage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-col py-4">
-                                    <Text className="text-lg pb-1">性別</Text>
+                                    <Text className="text-lg pb-1">{t('accountSettings.gender')}</Text>
                                     <Text style={{ color: '#555555' }}>{genderText}</Text>
                                 </View>
                                 <RightArrowIconSvg />
@@ -100,8 +109,7 @@ const AccountSettingPageComponent = () => {
                         <Pressable onPress={() => router.push('changeCarPage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-col py-4">
-                                    <Text className="text-lg pb-1">車牌號碼</Text>
-
+                                    <Text className="text-lg pb-1">{t('accountSettings.license_plate')}</Text>
                                     <Text style={{ color: '#555555' }}>{user?.car}</Text>
                                 </View>
                                 <RightArrowIconSvg />
@@ -113,16 +121,16 @@ const AccountSettingPageComponent = () => {
                         {/* <Pressable onPress={() => router.push('changePhonePage')}>
                             <View className="flex-row items-center justify-between">
                                 <View className="flex-co py-4">
-                                    <Text className="text-lg pb-1">電話號碼</Text>
+                                    <Text className="text-lg pb-1">{t('accountSettings.phone')}</Text>
                                     <Text style={{ color: '#555555' }}>
-                                        {user?.phone ? `+852 ${user.phone}` : 'No phone number provided'}
+                                        {user?.phone ? `+852 ${user.phone}` : t('accountSettings.no_phone')}
                                     </Text>
                                 </View>
                                 <RightArrowIconSvg />
                             </View>
                         </Pressable>
                         <View className="h-0.5  bg-[#f4f4f4]" /> */}
-                        <Button onPress={handleDeletionAccount} title="刪除帳戶" color="red"></Button>
+                        <Button onPress={handleDeletionAccount} title={t('accountSettings.delete_account')} color="red"></Button>
                     </View>
                 </View>
             </ScrollView>
@@ -130,4 +138,4 @@ const AccountSettingPageComponent = () => {
     );
 };
 
-export default AccountSettingPageComponent;
+export default AccountSettingPageComponent;

+ 6 - 4
component/accountPages/activityRecordPageComponent.tsx

@@ -6,9 +6,11 @@ import CouponTabViewComponent from '../global/couponTabView';
 import { useEffect, useState } from 'react';
 import { walletService } from '../../service/walletService';
 import BookingTabViewComponent from '../global/bookingTabViewComponent';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const ActivityRecordPageComponent = () => {
     const screenHeight = Dimensions.get('window').height;
+    const { t } = useTranslation();
 
     return (
         <SafeAreaView className="flex-1 bg-white" edges={['top']}>
@@ -25,15 +27,15 @@ const ActivityRecordPageComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginTop: 25, marginBottom: 10 }}>充電記錄</Text>
+                    <Text style={{ fontSize: 45, marginTop: 25, marginBottom: 10 }}>{t('chargingHistory.charging_records')}</Text>
                 </View>
                 <View className="flex-1">
-                    <BookingTabViewComponent titles={['已預約', '已完成']} />
+                    <BookingTabViewComponent />
                 </View>
-                <View style={{ width: "100%",height: 130 }} />
+                <View style={{ width: "100%",height: 150 }} />
             </View>
         </SafeAreaView>
     );
 };
 
-export default ActivityRecordPageComponent;
+export default ActivityRecordPageComponent;

+ 18 - 16
component/accountPages/changeCarPageComponent.tsx

@@ -10,6 +10,7 @@ import { authenticationService } from '../../service/authService';
 import * as SecureStore from 'expo-secure-store';
 import { chargeStationService } from '../../service/chargeStationService';
 import { User } from '../../types/user';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const ChangeCarPageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
@@ -17,6 +18,7 @@ const ChangeCarPageComponent = () => {
     const [car, setCar] = useState<string | null>(null);
     const [isLoading, setIsLoading] = useState(false);
     const [error, setError] = useState<string | null>(null);
+    const { t } = useTranslation();
 
     useEffect(() => {
         const getToken = async () => {
@@ -28,23 +30,23 @@ const ChangeCarPageComponent = () => {
 
     const validateLicensePlate = (licensePlate: string | null): string | null => {
         if (!licensePlate || licensePlate.trim() === '') {
-            return '請輸入車牌號碼';
+            return t('accountSettings.change_car.enter_license_plate_error');
         }
 
         const trimmedPlate = licensePlate.trim();
 
         if (trimmedPlate.length < 2) {
-            return '車牌號碼至少需要2個字符';
+            return t('accountSettings.change_car.license_plate_min_length');
         }
 
         if (trimmedPlate.length > 10) {
-            return '車牌號碼不能超過10個字符';
+            return t('accountSettings.change_car.license_plate_max_length');
         }
 
         // Check for special characters (allow letters, numbers, hyphen, and spaces)
         const validFormat = /^[A-Za-z0-9-\s]+$/;
         if (!validFormat.test(trimmedPlate)) {
-            return '車牌號碼只能包含字母、數字和空格';
+            return t('accountSettings.change_car.license_plate_invalid_format');
         }
 
         return null;
@@ -54,7 +56,7 @@ const ChangeCarPageComponent = () => {
         const validationError = validateLicensePlate(licensePlate);
         if (validationError) {
             setError(validationError);
-            Alert.alert('錯誤', validationError);
+            Alert.alert(t('common.error'), validationError);
             return;
         }
 
@@ -67,9 +69,9 @@ const ChangeCarPageComponent = () => {
             );
             if (response === true) {
                 setError(null);
-                Alert.alert('成功', '車牌號碼保存成功', [
+                Alert.alert(t('common.success'), t('accountSettings.change_car.save_success'), [
                     {
-                        text: 'OK',
+                        text: t('common.ok'),
                         onPress: () => router.replace('accountMainPage')
                     }
                 ]);
@@ -79,12 +81,12 @@ const ChangeCarPageComponent = () => {
                     ...newUser,
                 } as User);
             } else {
-                setError('無法保存車牌號碼');
-                Alert.alert('錯誤', '無法保存車牌號碼,請稍後再試');
+                setError(t('accountSettings.change_car.save_failed'));
+                Alert.alert(t('common.error'), t('accountSettings.change_car.save_failed'));
             }
         } catch (error) {
-            setError('暫時無法保存車牌號碼');
-            Alert.alert('錯誤', '暫時無法保存車牌號碼,請稍後再試');
+            setError(t('accountSettings.change_car.temporary_save_failed'));
+            Alert.alert(t('common.error'), t('accountSettings.change_car.temporary_save_failed'));
         }
     };
 
@@ -103,11 +105,11 @@ const ChangeCarPageComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginVertical: 25 }}>更改車牌號碼</Text>
-                    <Text className="text-xl ">請輸入新車牌號碼</Text>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('accountSettings.change_car.change_license_plate_title')}</Text>
+                    <Text className="text-xl ">{t('accountSettings.change_car.enter_new_license_plate')}</Text>
                     <View className="py-2">
                         <NormalInput
-                            placeholder={'請輸入新車牌號碼'}
+                            placeholder={t('accountSettings.change_car.enter_new_license_plate_placeholder')}
                             onChangeText={(t) => {
                                 setCar(t);
                                 setError(null); // Clear error when user types
@@ -117,7 +119,7 @@ const ChangeCarPageComponent = () => {
                         {error && <Text className="text-red-500 mt-1">{error}</Text>}
                     </View>
                     <NormalButton
-                        title={<Text className="text-white">{isLoading ? '更改中...' : '確認'}</Text>}
+                        title={<Text className="text-white">{isLoading ? t('accountSettings.change_car.changing') : t('common.confirm')}</Text>}
                         disabled={isLoading}
                         onPress={() => saveLicensePlate(car)}
                     />
@@ -127,4 +129,4 @@ const ChangeCarPageComponent = () => {
     );
 };
 
-export default ChangeCarPageComponent;
+export default ChangeCarPageComponent;

+ 17 - 10
component/accountPages/changeEmailPageComponent.tsx

@@ -8,12 +8,16 @@ import NormalInput from '../global/normal_input';
 import NormalButton from '../global/normal_button';
 import { authenticationService } from '../../service/authService';
 import * as SecureStore from 'expo-secure-store';
+import { useTranslation } from '../../util/hooks/useTranslation';
+
 const ChangeEmailPageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
     const [token, setToken] = useState<string | null>(null);
     const [email, setName] = useState<string | null>(null);
     const [isLoading, setIsLoading] = useState(false);
     const [error, setError] = useState<string | null>(null);
+    const { t } = useTranslation();
+
     useEffect(() => {
         const getToken = async () => {
             const storedToken = await SecureStore.getItemAsync('accessToken');
@@ -22,13 +26,13 @@ const ChangeEmailPageComponent = () => {
         getToken();
     }, []);
 
-    const handleChangeEmail= async () => {
+    const handleChangeEmail = async () => {
         if (!email) {
-            setError('請輸入新的郵箱');
+            setError(t('accountSettings.change_email.enter_new_email_error'));
             return;
         }
         if (!token) {
-            setError('未找到有效的登錄令牌,請重新登錄');
+            setError(t('accountSettings.change_email.no_token_error'));
             return;
         }
         setError(null);
@@ -44,11 +48,11 @@ const ChangeEmailPageComponent = () => {
                 }
                 router.replace('accountMainPage');
             } else {
-                setError('更新郵箱失敗,請稍後再試');
+                setError(t('accountSettings.change_email.update_failed'));
             }
         } catch (error) {
             console.error('Error changing name:', error);
-            setError('發生錯誤,請稍後再試');
+            setError(t('accountSettings.change_email.general_error'));
         } finally {
             setIsLoading(false);
         }
@@ -73,20 +77,23 @@ const ChangeEmailPageComponent = () => {
                         <CrossLogoSvg />
                     </Pressable>
                     <Text style={{ fontSize: 45, marginVertical: 25 }}>
-                        更改郵箱
+                        {t('accountSettings.change_email.change_email_title')}
                     </Text>
-                    <Text className="text-xl ">請輸入新郵箱</Text>
+                    <Text className="text-xl ">{t('accountSettings.change_email.enter_new_email')}</Text>
                     <View className="py-2">
                         <NormalInput
-                            placeholder={user?.email}
+                            placeholder={user?.email || ''}
                             onChangeText={(t) => setName(t)}
                             editable={!isLoading}
                         />
                     </View>
+                    {error && (
+                        <Text className="text-red-500 py-2">{error}</Text>
+                    )}
                     <NormalButton
                         title={
                             <Text className="text-white">
-                                {isLoading ? '更改中...' : '確認'}
+                                {isLoading ? t('accountSettings.change_email.changing') : t('common.confirm')}
                             </Text>
                         }
                         disabled={isLoading}
@@ -98,4 +105,4 @@ const ChangeEmailPageComponent = () => {
     );
 };
 
-export default ChangeEmailPageComponent;
+export default ChangeEmailPageComponent;

+ 15 - 11
component/accountPages/changeGenderPageComponent.tsx

@@ -8,6 +8,7 @@ import NormalButton from '../global/normal_button';
 import * as SecureStore from 'expo-secure-store';
 import { authenticationService } from '../../service/authService';
 import DropdownSelect from '../global/dropdown_select';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const ChangeGenderPageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
@@ -15,10 +16,11 @@ const ChangeGenderPageComponent = () => {
     const [gender, setGender] = useState<string | null>(null);
     const [isLoading, setIsLoading] = useState(false);
     const [error, setError] = useState<string | null>(null);
+    const { t } = useTranslation();
 
     const dropdownOption = [
-        { label: '男', value: 'man' },
-        { label: '女', value: 'woman' }
+        { label: t('accountSettings.change_gender.male'), value: 'man' },
+        { label: t('accountSettings.change_gender.female'), value: 'woman' }
     ];
 
     useEffect(() => {
@@ -28,13 +30,14 @@ const ChangeGenderPageComponent = () => {
         };
         getToken();
     }, []);
+
     const handleChangeGender = async () => {
         if (!gender) {
-            setError('請選擇性別');
+            setError(t('accountSettings.change_gender.select_gender_error'));
             return;
         }
         if (!token) {
-            setError('未找到有效的登錄令牌,請重新登錄');
+            setError(t('accountSettings.change_gender.no_token_error'));
             return;
         }
         setError(null);
@@ -53,15 +56,16 @@ const ChangeGenderPageComponent = () => {
                 }
                 router.replace('accountMainPage');
             } else {
-                setError('更新性別失敗,請稍後再試');
+                setError(t('accountSettings.change_gender.update_failed'));
             }
         } catch (error) {
             console.error('Error changing gender:', error);
-            setError('發生錯誤,請稍後再試');
+            setError(t('accountSettings.change_gender.general_error'));
         } finally {
             setIsLoading(false);
         }
     };
+
     return (
         <SafeAreaView
             className="flex-1 bg-white"
@@ -81,14 +85,14 @@ const ChangeGenderPageComponent = () => {
                         <CrossLogoSvg />
                     </Pressable>
                     <Text style={{ fontSize: 45, marginVertical: 25 }}>
-                        更改性別
+                        {t('accountSettings.change_gender.change_gender_title')}
                     </Text>
-                    <Text className="text-xl ">請選擇新性別</Text>
+                    <Text className="text-xl ">{t('accountSettings.change_gender.select_new_gender')}</Text>
                     <View className="py-2">
                         <View className="flex-1 ">
                             <DropdownSelect
                                 dropdownOptions={dropdownOption}
-                                placeholder="性別"
+                                placeholder={t('accountSettings.change_gender.gender')}
                                 onSelect={(t) => {
                                     setGender(t);
                                 }}
@@ -99,7 +103,7 @@ const ChangeGenderPageComponent = () => {
                     <NormalButton
                         title={
                             <Text className="text-white text-lg">
-                                {isLoading ? '更改中...' : '確認'}
+                                {isLoading ? t('accountSettings.change_gender.changing') : t('common.confirm')}
                             </Text>
                         }
                         onPress={handleChangeGender}
@@ -116,4 +120,4 @@ const ChangeGenderPageComponent = () => {
     );
 };
 
-export default ChangeGenderPageComponent;
+export default ChangeGenderPageComponent;

+ 16 - 9
component/accountPages/changeNamePageComponent.tsx

@@ -8,12 +8,16 @@ import NormalInput from '../global/normal_input';
 import NormalButton from '../global/normal_button';
 import { authenticationService } from '../../service/authService';
 import * as SecureStore from 'expo-secure-store';
+import { useTranslation } from '../../util/hooks/useTranslation';
+
 const ChangeNamePageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
     const [token, setToken] = useState<string | null>(null);
     const [name, setName] = useState<string | null>(null);
     const [isLoading, setIsLoading] = useState(false);
     const [error, setError] = useState<string | null>(null);
+    const { t } = useTranslation();
+
     useEffect(() => {
         const getToken = async () => {
             const storedToken = await SecureStore.getItemAsync('accessToken');
@@ -24,11 +28,11 @@ const ChangeNamePageComponent = () => {
 
     const handleChangeName = async () => {
         if (!name) {
-            setError('請輸入新的暱稱');
+            setError(t('accountSettings.change_name.enter_new_nickname_error'));
             return;
         }
         if (!token) {
-            setError('未找到有效的登錄令牌,請重新登錄');
+            setError(t('accountSettings.change_name.no_token_error'));
             return;
         }
         setError(null);
@@ -44,11 +48,11 @@ const ChangeNamePageComponent = () => {
                 }
                 router.replace('accountMainPage');
             } else {
-                setError('更新暱稱失敗,請稍後再試');
+                setError(t('accountSettings.change_name.update_failed'));
             }
         } catch (error) {
             console.error('Error changing name:', error);
-            setError('發生錯誤,請稍後再試');
+            setError(t('accountSettings.change_name.general_error'));
         } finally {
             setIsLoading(false);
         }
@@ -73,20 +77,23 @@ const ChangeNamePageComponent = () => {
                         <CrossLogoSvg />
                     </Pressable>
                     <Text style={{ fontSize: 45, marginVertical: 25 }}>
-                        更改暱稱
+                        {t('accountSettings.change_name.change_nickname_title')}
                     </Text>
-                    <Text className="text-xl ">請輸入新名稱</Text>
+                    <Text className="text-xl ">{t('accountSettings.change_name.enter_new_name')}</Text>
                     <View className="py-2">
                         <NormalInput
-                            placeholder={user?.nickname}
+                            placeholder={user?.nickname || ''}
                             onChangeText={(t) => setName(t)}
                             editable={!isLoading}
                         />
                     </View>
+                    {error && (
+                        <Text className="text-red-500 py-2">{error}</Text>
+                    )}
                     <NormalButton
                         title={
                             <Text className="text-white">
-                                {isLoading ? '更改中...' : '確認'}
+                                {isLoading ? t('accountSettings.change_name.changing') : t('common.confirm')}
                             </Text>
                         }
                         disabled={isLoading}
@@ -98,4 +105,4 @@ const ChangeNamePageComponent = () => {
     );
 };
 
-export default ChangeNamePageComponent;
+export default ChangeNamePageComponent;

+ 18 - 25
component/accountPages/changePasswordPageComponent.tsx

@@ -8,6 +8,7 @@ import NormalInput from '../global/normal_input';
 import NormalButton from '../global/normal_button';
 import * as SecureStore from 'expo-secure-store';
 import { authenticationService } from '../../service/authService';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const changePasswordPageComponent = () => {
     const { user, setUser } = useContext(AuthContext);
@@ -19,6 +20,7 @@ const changePasswordPageComponent = () => {
     const [error, setError] = useState<string | null>(null);
     const [isLoading, setIsLoading] = useState(false);
     const [isLoading2, setIsLoading2] = useState(false);
+    const { t } = useTranslation();
 
     useEffect(() => {
         const getToken = async () => {
@@ -32,11 +34,11 @@ const changePasswordPageComponent = () => {
         try {
             setIsLoading(true);
             if (!currentPassword) {
-                setError('請輸入密碼');
+                setError(t('accountSettings.change_password.enter_password_error'));
                 return;
             }
             if (!user || !user.email) {
-                setError('無法驗證用戶,請重新登錄');
+                setError(t('accountSettings.change_password.user_verification_error'));
                 return;
             }
 
@@ -46,11 +48,11 @@ const changePasswordPageComponent = () => {
                 setError('');
                 return true;
             } else {
-                setError('密碼錯誤,請稍後再試');
+                setError(t('accountSettings.change_password.incorrect_password'));
             }
         } catch (error) {
             console.error('Error verifying password:', error);
-            setError('密碼錯誤,請稍後再試');
+            setError(t('accountSettings.change_password.incorrect_password'));
             return false;
         } finally {
             setIsLoading(false);
@@ -61,12 +63,12 @@ const changePasswordPageComponent = () => {
         try {
             setIsLoading2(true);
             if (newPassword !== newConfirmPassword) {
-                setError('新密碼不相符');
+                setError(t('accountSettings.change_password.password_mismatch'));
                 return;
             }
 
             if (!newConfirmPassword || !token) {
-                setError('請輸入新密碼');
+                setError(t('accountSettings.change_password.enter_new_password'));
                 return;
             }
 
@@ -76,11 +78,11 @@ const changePasswordPageComponent = () => {
                 setError('');
                 router.replace('accountMainPage');
             } else {
-                setError('密碼更改失敗,請稍後再試');
+                setError(t('accountSettings.change_password.change_password_failed'));
             }
         } catch (error) {
             console.error('Error changing password:', error);
-            setError('發生錯誤,請稍後再試');
+            setError(t('accountSettings.change_password.general_error'));
         } finally {
             setIsLoading2(false);
         }
@@ -101,11 +103,11 @@ const changePasswordPageComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginVertical: 25 }}>更改密碼</Text>
-                    <Text className="text-xl ">請輸入您現時的帳戶密碼</Text>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('accountSettings.change_password.change_password_title')}</Text>
+                    <Text className="text-xl ">{t('accountSettings.change_password.enter_current_password')}</Text>
                     <View className="py-2">
                         <NormalInput
-                            placeholder="帳戶密碼"
+                            placeholder={t('accountSettings.change_password.account_password')}
                             onChangeText={(t) => setCurrentPassword(t)}
                             secureTextEntry={true}
                             editable={!isPasswordVerified}
@@ -115,7 +117,7 @@ const changePasswordPageComponent = () => {
                     <NormalButton
                         title={
                             <Text style={{ color: '#fff' }}>
-                                {isLoading ? '驗證中...' : isPasswordVerified ? '已驗證' : '下一步'}
+                                {isLoading ? t('accountSettings.change_password.verifying') : isPasswordVerified ? t('accountSettings.change_password.verified') : t('common.next')}
                             </Text>
                         }
                         onPress={() => {
@@ -136,19 +138,19 @@ const changePasswordPageComponent = () => {
                         ]}
                     >
                         <NormalInput
-                            placeholder="新密碼"
+                            placeholder={t('accountSettings.change_password.new_password')}
                             onChangeText={(t) => setNewPassword(t)}
                             secureTextEntry={true}
                             textContentType={'oneTimeCode'}
                         />
                         <NormalInput
-                            placeholder="確認密碼"
+                            placeholder={t('accountSettings.change_password.confirm_password')}
                             onChangeText={(t) => setNewConfirmPassword(t)}
                             secureTextEntry={true}
                             textContentType={'oneTimeCode'}
                         />
                         <NormalButton
-                            title={<Text style={{ color: '#fff' }}>{isLoading2 ? '更改中...' : '確認'}</Text>}
+                            title={<Text style={{ color: '#fff' }}>{isLoading2 ? t('accountSettings.change_password.changing') : t('common.confirm')}</Text>}
                             onPress={handlePasswordChange}
                         />
                     </View>
@@ -204,13 +206,4 @@ const styles = StyleSheet.create({
     footer: { color: '#02677D', fontSize: 16, paddingVertical: 10 }
 });
 
-export default changePasswordPageComponent;
-
-// <NormalButton
-// title={<Text className="text-white text-lg">確認</Text>}
-// onPress={() => {
-//     // poSSIBLY ADD ERROR HANDLING AND LOADING
-//     // authenticationService.changePassword(password, token);
-//     router.replace('accountMainPage');
-// }}
-// />
+export default changePasswordPageComponent;

+ 33 - 19
component/accountPages/chargingDetailsPageComponent.tsx

@@ -6,12 +6,16 @@ import { useEffect, useMemo, useState, useRef } from 'react';
 import { chargeStationService } from '../../service/chargeStationService';
 import { ChargingDetails, Remark, ElectricityPrice, Special } from '../../service/type/chargeStationType';
 import { format, parseISO } from 'date-fns';
+import { useTranslation } from '../../util/hooks/useTranslation';
+
 const ChargingDetailsPageComponent = () => {
     const screenHeight = Dimensions.get('window').height;
     const params = useLocalSearchParams();
     const [list, setList] = useState<ChargingDetails>({} as ChargingDetails);
     const [remark, setRemark] = useState<Remark>({} as Remark);
     const [time, setTime] = useState<string>('');
+    const { t } = useTranslation();
+
   useEffect(() => {
     const fetchData = async () => {
       const res = await chargeStationService.fetchChargingDetails(params.id.toString())
@@ -40,13 +44,14 @@ const ChargingDetailsPageComponent = () => {
           // 格式化为指定格式
           formattedDate = `${format(startTime, 'yyyy/MM/dd HH:mm:ss')}-${format(endTime, 'HH:mm:ss')}`;
         } else {
-          formattedDate = 'Invalid Time';
+          formattedDate = t('chargingHistory.charging_details.invalid_time');
         }
         setTime(formattedDate)
       }
     };
     fetchData();
   }, [])
+
   const totalPrice = useMemo(() => {
     if (list.promotion_name) {
       const price = list?.connector?.EquipmentID?.StationID?.price
@@ -63,12 +68,14 @@ const ChargingDetailsPageComponent = () => {
       return '0';
     }
   }, [list]);
+
   const couponPrice = useMemo(() => {
     if (list.promotion_name) {
       let actual_fee = (list.total_fee - list.withdraw_fee) > 0 ? (list.total_fee - list.withdraw_fee) : 0;
       return actual_fee.toFixed(1)
     }
   }, [list])
+
     return (
         <SafeAreaView className="flex-1 bg-white" edges={['top']}>
           <ScrollView>
@@ -82,6 +89,7 @@ const ChargingDetailsPageComponent = () => {
                               router.replace('/accountMainPage');
                           }
                       }}
+                      className='ml-2'
                   >
                     <CrossLogoSvg />
                   </Pressable>
@@ -97,20 +105,20 @@ const ChargingDetailsPageComponent = () => {
                     <Text style={styles.totalPrice}>{list.promotion_name? couponPrice:totalPrice}</Text>
                     <View style={styles.viewLine}></View>
                     <View className='w-full flex-row justify-between mt-6'>
-                      <Text style={styles.leftLable}>訂單编號: </Text>
+                      <Text style={styles.leftLable}>{t('chargingHistory.charging_details.order_number')}: </Text>
                       <View style={{ flex: 1, marginLeft: 5 }}>
                         <Text style={styles.rightLable}>{list.format_order_id}</Text>
                       </View>
                     </View>
                     <View className='w-full flex-row justify-between my-3'>
-                      <Text style={styles.leftLable}>充電時間: </Text>
+                      <Text style={styles.leftLable}>{t('chargingHistory.charging_details.charging_time')}: </Text>
                       <View style={{ flex: 1, marginLeft: 5 }}>
                         <Text style={styles.rightLable}>{time}</Text>
                       </View>
 
                     </View>
                     <View className='w-full flex-row justify-between mb-4'>
-                      <Text style={styles.leftLable}>充電站位置:</Text>
+                      <Text style={styles.leftLable}>{t('chargingHistory.charging_details.charging_station')}:</Text>
                       <View style={{ flex: 1, marginLeft: 5 }}>
                         <Text style={styles.rightLable}>{params.chargeStationName}</Text>
                       </View>
@@ -120,8 +128,8 @@ const ChargingDetailsPageComponent = () => {
                     <View style={styles.viewLine}></View>
                     <View className='w-full flex-row justify-between mt-6'>
                       <View>
-                        <Text style={styles.leftLable}>實付:</Text>
-                        {list.promotion_name ? <Text style={{fontSize: 12, color:'#888888'}}>優惠券支付</Text>: null}
+                        <Text style={styles.leftLable}>{t('chargingHistory.charging_details.amount_paid')}:</Text>
+                        {list.promotion_name ? <Text style={{fontSize: 12, color:'#888888'}}>{t('chargingHistory.charging_details.coupon_payment')}</Text>: null}
                       </View>
                       <Text style={styles.rightLable}>${list.promotion_name? couponPrice:totalPrice}</Text>
                     </View>
@@ -132,11 +140,13 @@ const ChargingDetailsPageComponent = () => {
         </SafeAreaView>
     );
 };
+
 interface ChargingDataComponentProps {
     list: ChargingDetails;
     totalPrice: string;
     remark: Remark;
 }
+
 const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
     list,
     totalPrice,
@@ -146,6 +156,8 @@ const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
   const [rush, setRush] = useState<Special>({} as Special)
   const [elses, setElses ] = useState<Special>({} as Special)
   const [off, setOff] = useState<Special>({} as Special)
+  const { t } = useTranslation();
+
   useEffect(() => {
     if (!list.promotion_name && !hasFetchedPrice.current && list.actual_start_time) {
       chargeStationService.fetchElectricityPrice(list.pricemodel_id || 'a').then(res => {
@@ -176,15 +188,15 @@ const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
         {(remark.RushKwh) ? 
         <View>
           <View className='w-full flex-row justify-between mt-4'>
-            <Text style={styles.leftLable}>峰時總電量: </Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.peak_electricity')}: </Text>
             <Text style={styles.rightLable}>{remark.RushKwh?.toFixed(1)}</Text>
           </View>
           <View className='w-full flex-row justify-between my-3'>
-              <Text style={styles.leftLable}>峰時電價({rush.from}-{rush.to}):</Text>
+              <Text style={styles.leftLable}>{t('chargingHistory.charging_details.peak_rate')}({rush.from}-{rush.to}):</Text>
             <Text style={styles.rightLable}>${rush.price}</Text>
           </View>
           <View className='w-full flex-row justify-between mb-3'>
-            <Text style={styles.leftLable}>峰時總電費:</Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.peak_fee')}:</Text>
             
             <Text style={styles.rightLable}>${remark.RushCharge?.toFixed(1)}</Text>
           </View>
@@ -192,16 +204,16 @@ const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
         {(remark.ElseKwh) ? 
         <View>
           <View className='w-full flex-row justify-between mt-4'>
-            <Text style={styles.leftLable}>平時總電量: </Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.flat_electricity')}: </Text>
             <Text style={styles.rightLable}>{remark.ElseKwh?.toFixed(1)}</Text>
           </View>
           <View className='w-full flex-row justify-between my-3'>
-            <Text style={styles.leftLable}>平時電價({elses.from}-{elses.to}):</Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.flat_rate')}({elses.from}-{elses.to}):</Text>
 
             <Text style={styles.rightLable}>${elses.price}</Text>
           </View>
           <View className='w-full flex-row justify-between mb-3'>
-            <Text style={styles.leftLable}>平時總電費:</Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.flat_fee')}:</Text>
             
             <Text style={styles.rightLable}>${remark.ElseCharge?.toFixed(1)}</Text>
           </View>
@@ -209,15 +221,15 @@ const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
         {(remark.OffKwh) ? 
         <View>
           <View className='w-full flex-row justify-between mt-4'>
-            <Text style={styles.leftLable}>穀時總電量: </Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.valley_electricity')}: </Text>
             <Text style={styles.rightLable}>{remark.OffKwh?.toFixed(1)}</Text>
           </View>
           <View className='w-full flex-row justify-between my-3'>
-              <Text style={styles.leftLable}>穀時電價({off.from}-{off.to}): </Text>
+              <Text style={styles.leftLable}>{t('chargingHistory.charging_details.valley_rate')}({off.from}-{off.to}): </Text>
             <Text style={styles.rightLable}>${off.price}</Text>
           </View>
           <View className='w-full flex-row justify-between mb-3'>
-            <Text style={styles.leftLable}>穀時總電費:</Text>    
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.valley_fee')}:</Text>    
             <Text style={styles.rightLable}>${remark.OffCharge?.toFixed(1)}</Text>
           </View>
         </View>: null }
@@ -227,15 +239,15 @@ const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
     return (
       <View>
         <View className='w-full flex-row justify-between mt-4'>
-            <Text style={styles.leftLable}>總電量: </Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.total_electricity')}: </Text>
             <Text style={styles.rightLable}>{remark.TotalPower?.toFixed(1)}</Text>
           </View>
           <View className='w-full flex-row justify-between my-3'>
-              <Text style={styles.leftLable}>電價: </Text>
+              <Text style={styles.leftLable}>{t('chargingHistory.charging_details.electricity_rate')}: </Text>
             <Text style={styles.rightLable}>${list?.connector?.EquipmentID?.StationID?.price}</Text>
           </View>
           <View className='w-full flex-row justify-between mb-3'>
-            <Text style={styles.leftLable}>總電費: </Text>
+            <Text style={styles.leftLable}>{t('chargingHistory.charging_details.total_fee')}: </Text>
             
             <Text style={styles.rightLable}>${totalPrice}</Text>
           </View>
@@ -244,6 +256,7 @@ const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
   }
 
 }
+
 const styles = StyleSheet.create({
   viewLine: {
     width: '100%',
@@ -266,4 +279,5 @@ const styles = StyleSheet.create({
     textAlign: 'right',
   },
 })
-export default ChargingDetailsPageComponent;
+
+export default ChargingDetailsPageComponent;

+ 2 - 2
component/accountPages/userTermsPageComponent.tsx

@@ -24,7 +24,7 @@ const TermsSection: React.FC<TermsSectionProps> = ({ title, clauses }) => (
   </View>
 );
 const UserTermsPageComponent: React.FC = () => {
-  const { getCurrentLanguageConfig } = useTranslation()
+  const { getCurrentLanguageConfig, t } = useTranslation()
   const curren = getCurrentLanguageConfig()
   const sectionsZh = [
     {
@@ -235,7 +235,7 @@ const UserTermsPageComponent: React.FC = () => {
                 color: '#fff'
             }}
         >
-            同意並繼續
+          {t("common.agree")}
         </Text>}
         >
 

+ 27 - 18
component/chargingPage/penaltyPaymentPageComponent.tsx

@@ -6,17 +6,21 @@ import { useEffect, useState } from 'react';
 import { chargeStationService } from '../../service/chargeStationService';
 import useUserInfoStore from '../../providers/userinfo_store';
 import { PreviousPageBlackSvg } from '../global/SVG';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const PenaltyPaymentPageComponent = () => {
     const params = useLocalSearchParams();
     const { userID } = useUserInfoStore();
     const navigation = useNavigation();
     const [leaving_time, setLeavingTime] = useState<string | null>(null)
+    const { t } = useTranslation();
+
     useEffect(() => {
         navigation.setOptions({
             gestureEnabled: false
         });
     }, [navigation]);
+
     useEffect(() => {
         chargeStationService.fetchChargingDetails(params.id.toString()).then(res => {
           if (res && res[0].charging.length > 0) {
@@ -25,6 +29,7 @@ const PenaltyPaymentPageComponent = () => {
           }
         })
     }, [params]);
+
     const convertBookingDateTime = (isoDateString: string): { date: string; time: string } => {
         const bookingDate = new Date(isoDateString);
 
@@ -71,17 +76,17 @@ const PenaltyPaymentPageComponent = () => {
             const result = await chargeStationService.payPenalty(payload);
             console.log('Payment result:', result);
             if (result.status) {
-                Alert.alert('支付成功', '罰款已成功支付', [
-                    { text: '確認', onPress: () => router.replace('/mainPage') }
+                Alert.alert(t('penalty_payment.payment_success'), t('penalty_payment.penalty_paid'), [
+                    { text: t('common.confirm'), onPress: () => router.replace('/mainPage') }
                 ]);
             } else {
-                Alert.alert('支付失敗', '餘額不足,即將跳轉到錢包', [
-                    { text: '確認', onPress: () => router.push('/(account)/(wallet)/walletPage') }
+                Alert.alert(t('penalty_payment.payment_failed'), t('penalty_payment.insufficient_balance'), [
+                    { text: t('common.confirm'), onPress: () => router.push('/(account)/(wallet)/walletPage') }
                 ]);
             }
         } catch (error) {
             console.error('Payment error:', error);
-            Alert.alert('支付錯誤', '發生錯誤,請稍後再試');
+            Alert.alert(t('common.error'), t('penalty_payment.payment_error'));
         }
     };
 
@@ -100,14 +105,14 @@ const PenaltyPaymentPageComponent = () => {
                     >
                         <PreviousPageBlackSvg />
                     </Pressable>
-                    <Text className="text-3xl my-8 text-center">尚未繳付罰款的充電記錄</Text>
+                    <Text className="text-3xl my-8 text-center">{t('penalty_payment.unpaid_penalty_title')}</Text>
                 </View>
 
                 <View className="flex-1 mt-4 mx-[5%]">
                     <View className="flex-1 flex-row items-center pb-3">
                         <View className="flex-1 flex-column">
                             <Text style={styles.grayColor} className="text-base">
-                                實際充電到期時間
+                                {t('penalty_payment.actual_end_time')}
                             </Text>
                             <Text style={styles.greenColor} className="text-4xl text-center  pt-2">
                                 {actual_end_time}
@@ -115,7 +120,7 @@ const PenaltyPaymentPageComponent = () => {
                         </View>
                         <View className="flex-1 flex-column">
                             <Text style={styles.grayColor} className="text-base pl-7">
-                                實際充電結束時間
+                                {t('penalty_payment.actual_charging_end_time')}
                             </Text>
                             <Text style={styles.greenColor} className="text-4xl text-center pt-2">
                                 {leaving_time || user_end_time}
@@ -124,7 +129,7 @@ const PenaltyPaymentPageComponent = () => {
                     </View>
                     <View className="flex-1 flex-column justify-center space-y-1 pb-3">
                         <Text style={styles.grayColor} className="text-base">
-                            充電日期
+                            {t('penalty_payment.charging_date')}
                         </Text>
                         <Text style={styles.greenColor} className="text-base">
                             {date}
@@ -132,7 +137,7 @@ const PenaltyPaymentPageComponent = () => {
                     </View>
                     {/* <View className="flex-1 flex-column justify-center space-y-1 pb-3">
                         <Text style={styles.grayColor} className="text-base">
-                            充電地點
+                            {t('penalty_payment.charging_location')}
                         </Text>
                         <Text style={styles.greenColor} className="text-base ">
                             Crazy Charge(偉業街)
@@ -140,7 +145,7 @@ const PenaltyPaymentPageComponent = () => {
                     </View> */}
                     <View className="flex-1 flex-column justify-center space-y-1 pb-3">
                         <Text style={styles.grayColor} className="text-base">
-                            罰款金額
+                            {t('penalty_payment.penalty_amount')}
                         </Text>
                         <Text style={styles.greenColor} className="text-lg ">
                             {params.penalty_fee}
@@ -148,7 +153,7 @@ const PenaltyPaymentPageComponent = () => {
                     </View>
                     <View className="flex-1 flex-column justify-center space-y-1 pb-3">
                         <Text style={styles.grayColor} className="text-base">
-                            訂單編號
+                            {t('penalty_payment.order_number')}
                         </Text>
                         <Text style={styles.greenColor} className=" ">
                             {params.format_order_id}
@@ -168,15 +173,19 @@ const PenaltyPaymentPageComponent = () => {
                                         fontWeight: '800'
                                     }}
                                 >
-                                    支付罰款
+                                    {t('penalty_payment.pay_penalty')}
                                 </Text>
                             }
                             // onPress={handlePayment}
                             onPress={() => {
-                                Alert.alert('將會從錢包餘額扣除款項以繳交罰款', '', [
-                                    { text: '取消', style: 'cancel' },
-                                    { text: '確認', onPress: handlePayment }
-                                ]);
+                                Alert.alert(
+                                    t('penalty_payment.payment_confirmation'), 
+                                    '', 
+                                    [
+                                        { text: t('common.cancel'), style: 'cancel' },
+                                        { text: t('common.confirm'), onPress: handlePayment }
+                                    ]
+                                );
                             }}
                             extendedStyle={{ padding: 24, marginTop: 24 }}
                         />
@@ -196,4 +205,4 @@ const styles = StyleSheet.create({
     greenColor: {
         color: '#02677D'
     }
-});
+});

+ 2 - 7
component/global/bookingTabViewComponent.tsx

@@ -6,10 +6,6 @@ import { chargeStationService } from '../../service/chargeStationService';
 
 const queryClient = new QueryClient();
 
-interface BookingTabViewComponentProps {
-    titles: string[];
-}
-
 // 更新 findStationByConnectorId 函数以增加安全性
 const findStationByConnectorId = (allStations: any[], targetConnectorId: string) => {
     if (!Array.isArray(allStations) || !targetConnectorId) {
@@ -119,7 +115,7 @@ const processReservations = (reservations: any [], allStations: string [], isFut
             } as TabItem;
         });
 };
-const BookingTabViewComponentInner: React.FC<BookingTabViewComponentProps> = ({ titles }) => {
+const BookingTabViewComponentInner: React.FC = () => {
     const { data, isLoading, error } = useQuery({
         queryKey: ['reservationsAndStations'],
         queryFn: fetchReservationsAndStations,
@@ -156,14 +152,13 @@ const BookingTabViewComponentInner: React.FC<BookingTabViewComponentProps> = ({
     console.log('All Reservation Items Processed:', allReservationItems);
     return (
         <TabViewComponent
-            titles={titles}
             tabItems={allReservationItems}
             isLoading={false}
         />
     );
 };
 
-const BookingTabViewComponent: React.FC<BookingTabViewComponentProps> = (props) => (
+const BookingTabViewComponent: React.FC = (props) => (
     <QueryClientProvider client={queryClient}>
         <BookingTabViewComponentInner {...props} />
     </QueryClientProvider>

+ 18 - 12
component/global/chargingRecord.tsx

@@ -17,6 +17,7 @@ import { FlashList } from '@shopify/flash-list';
 import { useEffect, useState } from 'react';
 import { calculateDistance } from './distanceCalculator';
 import { router } from 'expo-router';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 export interface TabItem {
     imgURL: ImageSourcePropType;
@@ -37,13 +38,11 @@ export interface TabItem {
 }
 
 interface TabViewComponentProps {
-    titles: string[];
     tabItems: TabItem[];
     isLoading?: boolean;
 }
 
 const TabViewComponent: React.FC<TabViewComponentProps> = ({
-    titles,
     isLoading,
     tabItems,
 }) => {
@@ -92,6 +91,7 @@ const TabViewComponent: React.FC<TabViewComponentProps> = ({
 
 const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Location.LocationObject | null }) => {
     const [distance, setDistance] = useState<number | null>(null);
+    const { t } = useTranslation();
 
     useEffect(() => {
         const getDistance = async () => {
@@ -116,7 +116,7 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
         >
             <View style={styles.container}>
                 <Image style={styles.image} source={item.imgURL} />
-                <View className="flex flex-col gap-2 mr-2">
+                <View className="flex flex-col flex-wrap gap-2 mr-2">
                     <Text
                         style={{
                             fontWeight: '700',
@@ -124,17 +124,20 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                             fontSize: 16
                         }}
                     >
-                        {`${item.date}日 - ${item.time}至${item.actual_end_time}`}
+                        {`${item.date}${t('chargingHistory.charging_record.day')} - ${item.time}${t('chargingHistory.charging_record.to')}${item.actual_end_time}`}
                     </Text>
-                    <View className="flex flex-row justify-between space-x-2">
+                    <View className="flex flex-row flex-wrap justify-between">
                         <Text
                             style={{
                                 fontWeight: '400',
                                 fontSize: 14,
-                                color: '#222222'
+                                color: '#222222',
+                                flexWrap: 'wrap',
+                                flexShrink: 1
                             }}
+                            numberOfLines={0}
                         >
-                            已充電度數:{' '}
+                            {t('chargingHistory.charging_record.charged_amount')}:{' '}
                             {item.actual_total_power
                                 ? item.actual_total_power % 1 === 0
                                     ? item.actual_total_power
@@ -145,10 +148,13 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                             style={{
                                 fontWeight: '400',
                                 fontSize: 14,
-                                color: '#222222'
+                                color: '#222222',
+                                flexWrap: 'wrap',
+                                flexShrink: 1
                             }}
+                            numberOfLines={0}
                         >
-                            應付金額:{' '}
+                            {t('chargingHistory.charging_record.amount_payable')}:{' '}
                             {item.actual_fee !== undefined && item.actual_fee !== null
                                 ? item.actual_fee <= 0
                                     ? '$0'
@@ -177,7 +183,7 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                                         color: '#02677D'
                                     }}
                                 >
-                                    訂單進行中
+                                    {t('chargingHistory.charging_record.order_in_progress')}
                                 </Text>
                                 : 
                                 <Text
@@ -190,7 +196,7 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                                         router.push({ pathname: 'chargingDetailsPage', params: { id: item.id, chargeStationName: item.chargeStationName }})
                                     }}
                                 >
-                                    訂單詳情 &gt;
+                                    {t('chargingHistory.charging_record.order_details')} &gt;
                                 </Text>
     
                             }
@@ -224,4 +230,4 @@ const styles = StyleSheet.create({
         marginTop: 20,
         marginRight: 8 // Add right margin to prevent text from touching the edge
     }
-});
+});

+ 4 - 5
component/global/displayedOnlyCouponTabView.tsx

@@ -18,10 +18,8 @@ import { IndividualCouponComponent } from '../accountPages/walletPageComponent';
 import { formatCouponDate } from '../../util/lib';
 import { useCallback, useEffect, useRef, useState } from 'react';
 import { walletService } from '../../service/walletService';
-import { useChargingStore } from '../../providers/scan_qr_payload_store';
-import { chargeStationService } from '../../service/chargeStationService';
 import { router } from 'expo-router';
-import axios from 'axios';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 export interface TabItem {
     imgURL: ImageSourcePropType;
@@ -45,6 +43,8 @@ const FirstRoute = ({
     loading: boolean;
     handleCouponClick: (couponName: string, couponDescription: string) => void;
 }) => {
+    const { t } = useTranslation(); // 使用翻译钩子
+    
     return (
         <View className="flex-1">
             <ScrollView
@@ -65,7 +65,7 @@ const FirstRoute = ({
                                         coupon.is_consumed === false &&
                                         new Date(coupon.expire_date) > new Date()
                                 ).length === 0 ? (
-                                    <Text className="pl-4">暫時戶口沒有優惠券。</Text>
+                                    <Text className="pl-4">{t('wallet.coupon.noCoupon')}</Text>
                                 ) : (
                                     coupons
                                         .filter(
@@ -131,7 +131,6 @@ const DisplayedOnlyCouponTabView: React.FC<TabViewComponentProps> = ({ titles })
     const [loading, setLoading] = useState(false);
     const [coupons, setCoupons] = useState([]);
     const [userID, setUserID] = useState('');
-    const [modalVisible, setModalVisible] = useState(false);
     const [useableCoupons, setUseableCoupons] = useState([]);
 
     useEffect(() => {

+ 1 - 1
component/homePage/homePage.tsx

@@ -435,7 +435,7 @@ const HomePage: React.FC<HomePageProps> = () => {
                             onPress={() => router.push('scanQrPage')}
                             // onPress={() => router.push('optionPage')}
                             title={
-                                <View className="flex flex-row justify-start">
+                                <View className="flex flex-row space-x-2 items-center">
                                     <QrCodeIconSvg />
                                     <Text className="text-white font-bold text-lg ml-2">{t('home.scan_and_charge')}</Text>
                                 </View>

+ 23 - 16
component/resultDetailPage/resultDetailPageComponent.tsx

@@ -19,8 +19,8 @@ import { CheckMarkLogoSvg, DirectionLogoSvg, PreviousPageSvg } from '../global/S
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { chargeStationService } from '../../service/chargeStationService';
 import * as Location from 'expo-location';
-import { calculateDistance } from '../global/distanceCalculator';
 import { ChargingDetails, Remark, PriceWeek, Special } from '../../service/type/chargeStationType';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 interface ChargingStationTabViewProps {
     titles: string[];
@@ -36,6 +36,8 @@ const ChargingStationTabView: React.FC<ChargingStationTabViewProps> = ({ titles,
     const layout = useWindowDimensions();
     const [list, setList] = useState<ChargingPeriod []>([])
     const [strWeek, setStrWeek] = useState<string>('')
+    const { t } = useTranslation();
+
     // 添加AM/PM标识但保持24小时制
     const addPeriodToTime = (timeString: string): string => {
         // 假设输入格式为 HH.mm 或 HH:mm
@@ -47,6 +49,7 @@ const ChargingStationTabView: React.FC<ChargingStationTabViewProps> = ({ titles,
         
         return `${timeString}${period}`;
     };
+    
     useEffect(() => { 
         chargeStationService.fetchElectricityPrice(pricemodel_id || 'a').then(res => {
             const date = new Date();
@@ -66,8 +69,8 @@ const ChargingStationTabView: React.FC<ChargingStationTabViewProps> = ({ titles,
         <ScrollView style={{ flex: 1, marginHorizontal: '5%' }}>
             <View>
                 <View className='w-full flex-row justify-between mt-4'>
-                    <Text style={styles.leftLable}>時段</Text>
-                    <Text style={styles.rightLable}>價格(/度)</Text>
+                    <Text style={styles.leftLable}>{t('charging.result_detail_page.time_period')}</Text>
+                    <Text style={styles.rightLable}>{t('charging.result_detail_page.price_per_kwh')}</Text>
                 </View>
                 {
                     list.map((item, index) => (
@@ -92,10 +95,12 @@ const ChargingStationTabView: React.FC<ChargingStationTabViewProps> = ({ titles,
         firstRoute: FirstRoute,
         secondRoute: SecondRoute
     });
+    
     const [routes] = React.useState([
         { key: 'firstRoute', title: titles[0] },
         { key: 'secondRoute', title: titles[1] }
     ]);
+    
     const [index, setIndex] = React.useState(0);
 
     const renderTabBar = (props: any) => (
@@ -107,13 +112,13 @@ const ChargingStationTabView: React.FC<ChargingStationTabViewProps> = ({ titles,
             }}
             style={{
                 backgroundColor: 'white',
-
                 elevation: 0,
                 marginHorizontal: 15,
                 borderBottomWidth: 0.5
             }}
         />
     );
+    
     return (
         <TabView
             navigationState={{ index, routes }}
@@ -152,6 +157,8 @@ const ResultDetailPageComponent = () => {
     const [currentLocation, setCurrentLocation] = useState<Location.LocationObject | null>(null);
     const [price, setPrice] = useState('');
     const [newAvailableConnectors, setNewAvailableConnectors] = useState<any>([]);
+    const { t } = useTranslation();
+
     useEffect(() => {
         const imgObj = imageSourceProps? {uri: imageSourceProps} : require('../../assets/dummyStationPicture.png')
         setImageSource(imgObj);
@@ -201,7 +208,7 @@ const ResultDetailPageComponent = () => {
             return () => {
                 isMounted = false;
             };
-        }, []) // Add any missing dependencies here if needed
+        }, [])
     );
 
     const handleNavigationPress = (StationLat: string, StationLng: string) => {
@@ -221,9 +228,9 @@ const ResultDetailPageComponent = () => {
                         Linking.openURL(webUrl).catch((err) => {
                             console.error('An error occurred', err);
                             Alert.alert(
-                                'Error',
-                                "Unable to open Google Maps. Please make sure it's installed on your device.",
-                                [{ text: 'OK' }],
+                                t('common.error'),
+                                t('charging.result_detail_page.unable_open_maps'),
+                                [{ text: t('common.ok') }],
                                 { cancelable: false }
                             );
                         });
@@ -270,7 +277,7 @@ const ResultDetailPageComponent = () => {
                             title={
                                 <View className="flex-row items-center justify-center text-center space-x-1">
                                     <DirectionLogoSvg />
-                                    <Text className="text-base ">路線</Text>
+                                    <Text className="text-base ">{t('charging.result_detail_page.route')}</Text>
                                 </View>
                             }
                             onPress={() => handleNavigationPress(stationLat, stationLng)}
@@ -296,12 +303,12 @@ const ResultDetailPageComponent = () => {
                             <View className="flex-1 flex-row ">
                                 <View className=" flex-1 flex-column  ustify-between">
                                     <Text className="text-xl " style={styles.text}>
-                                        收費
+                                        {t('charging.result_detail_page.charging_fee')}
                                     </Text>
 
                                     <View className="flex-row items-center space-x-2">
                                         <Text className="text-3xl text-[#02677D]">${price}</Text>
-                                        <Text style={styles.text}>每度電</Text>
+                                        <Text style={styles.text}>{t('charging.result_detail_page.per_kwh')}</Text>
                                     </View>
                                 </View>
                                 <View className="items-center justify-center">
@@ -309,12 +316,12 @@ const ResultDetailPageComponent = () => {
                                 </View>
                                 <View className=" flex-1 pl-4 flex-column  justify-between">
                                     <Text className="text-xl " style={styles.text}>
-                                        現時可用充電槍數目
+                                        {t('charging.result_detail_page.available_connectors')}
                                     </Text>
 
                                     <View className="flex-row items-center space-x-2">
                                         {isLoading ? (
-                                            <Text>Loading...</Text>
+                                            <Text>{t('charging.result_detail_page.loading')}</Text>
                                         ) : (
                                             // <ActivityIndicator />
                                             <Text className="text-3xl text-[#02677D]">
@@ -334,9 +341,9 @@ const ResultDetailPageComponent = () => {
 
                 <View className="min-h-[300px]">
                     <Text className="text-xl pb-2 mx-[5%]" style={styles.text}>
-                        充電站資訊
+                        {t('charging.result_detail_page.station_info')}
                     </Text>
-                    <ChargingStationTabView titles={['收費詳情', '其他']} pricemodel_id={pricemodel_id} />
+                    <ChargingStationTabView titles={[t('charging.result_detail_page.pricing_details'), t('charging.result_detail_page.others')]} pricemodel_id={pricemodel_id} />
                 </View>
             </ScrollView>
         </SafeAreaView>
@@ -363,4 +370,4 @@ const styles = StyleSheet.create({
         borderLeftWidth: 1,
         paddingLeft: 0,
     },
-});
+});

+ 159 - 2
i18n/locales/en/translation.json

@@ -1,4 +1,9 @@
 {
+  "tabs": {
+    "home": "Home",
+    "charging": "Charging",
+    "account": "Account"
+  },
   "login": {
     "tip1": "Tip: Old users",
     "tip2": "Must",
@@ -131,7 +136,8 @@
     "next": "Next Step >",
     "confirm": "Confirm",
     "cancel": "Cancel",
-    "title": ""
+    "title": "",
+    "agree": "Agree and continue"
   },
   "home": {
     "vehicle_info_error_title": "Unable to detect vehicle information",
@@ -221,7 +227,8 @@
       "cancel": "Cancel"
     },
     "coupon": {
-      "valid_until": "Valid until"
+      "valid_until": "Valid until",
+      "noCoupon": "There are currently no coupons available for household registration."
     },
     "error_fetching_coupons": "Error fetching coupons:",
     "payment_timeout_title": "Payment Timeout",
@@ -276,11 +283,161 @@
     "no_transactions_message": "You need to make at least one transaction to enjoy VIP exclusive benefits!",
     "error_fetching_transactions": "Error fetching transaction records:"
   },
+  "penalty_payment": {
+    "unpaid_penalty_title": "Unpaid Penalty Charging Record",
+    "actual_end_time": "Actual Charging End Time",
+    "actual_charging_end_time": "Actual Charging Completion Time",
+    "charging_date": "Charging Date",
+    "charging_location": "Charging Location",
+    "penalty_amount": "Penalty Amount",
+    "order_number": "Order Number",
+    "pay_penalty": "Pay Penalty",
+    "payment_success": "Payment Successful",
+    "penalty_paid": "Penalty has been successfully paid",
+    "payment_failed": "Payment Failed",
+    "insufficient_balance": "Insufficient balance, redirecting to wallet",
+    "payment_error": "An error occurred, please try again later",
+    "payment_confirmation": "The amount will be deducted from your wallet balance to pay the penalty"
+  },
   "charging": {
     "no_ongoing": {
       "title": "No Charging in Progress",
       "subtitle": "Go to a Crazy Charge station to start charging now!",
       "available_connectors": "Available Connectors"
+    },
+    "result_detail_page": {
+      "time_period": "Time Period",
+      "price_per_kwh": "Price (/kWh)",
+      "unable_open_maps": "Unable to open Google Maps. Please make sure it's installed on your device.",
+      "route": "Route",
+      "charging_fee": "Charging Fee",
+      "per_kwh": "per kWh",
+      "available_connectors": "Available Connectors",
+      "loading": "Loading...",
+      "station_info": "Station Information",
+      "pricing_details": "Pricing Details",
+      "others": "Others"
+    }
+  },
+  "account": {
+    "wallet": "Wallet",
+    "my_vehicle": "My Vehicle",
+    "charging_history": "Charging History",
+    "account_management": "Account Management",
+    "allow_notifications": "Allow Notifications",
+    "dark_mode": "Dark Mode",
+    "user_terms": "User Terms",
+    "logout": "Logout",
+    "notification_settings": "Notification Settings",
+    "notification_settings_message": "To change notification settings, please go to your phone's settings page and allow CrazyCharge notifications.",
+    "go_to_settings": "Go to Settings",
+    "notification_update_failed": "Failed to update notification permissions"
+  },
+  "chargingHistory": {
+    "charging_records": "Records",
+    "charging_record": {
+      "day": "",
+      "to": " to ",
+      "charged_amount": "Charged Amount",
+      "amount_payable": "Amount Payable",
+      "order_in_progress": "Order In Progress",
+      "order_details": "Order Details"
+    },
+    "charging_details": {
+      "invalid_time": "Invalid Time",
+      "order_number": "Order Number",
+      "charging_time": "Charging Time",
+      "charging_station": "Charging Station",
+      "amount_paid": "Amount Paid",
+      "coupon_payment": "Coupon Payment",
+      "peak_electricity": "Peak Electricity",
+      "peak_rate": "Peak Rate",
+      "peak_fee": "Peak Fee",
+      "flat_electricity": "Flat Electricity",
+      "flat_rate": "Flat Rate",
+      "flat_fee": "Flat Fee",
+      "valley_electricity": "Valley Electricity",
+      "valley_rate": "Valley Rate",
+      "valley_fee": "Valley Fee",
+      "total_electricity": "Total Electricity",
+      "electricity_rate": "Electricity Rate",
+      "total_fee": "Total Fee"
+    }
+  },
+  "accountSettings": {
+    "account_management": "Management",
+    "email": "Email Address",
+    "password": "Account Password",
+    "nickname": "Nickname",
+    "gender": "Gender",
+    "license_plate": "License Plate",
+    "phone": "Phone Number",
+    "no_phone": "No phone number provided",
+    "delete_account": "Delete Account",
+    "delete_account_confirmation": "Are you sure you want to delete your account? This action cannot be undone.",
+    "delete": "Delete",
+    "delete_account_error": "Failed to delete account. Please try again.",
+    "male": "Male",
+    "female": "Female",
+    "change_email": {
+      "change_email_title": "Change Email",
+      "enter_new_email": "Please enter new email",
+      "enter_new_email_error": "Please enter a new email",
+      "no_token_error": "No valid login token found, please log in again",
+      "update_failed": "Failed to update email, please try again later",
+      "general_error": "An error occurred, please try again later",
+      "changing": "Changing..."
+    },
+    "change_password": {
+      "change_password_title": "Change Password",
+      "enter_current_password": "Please enter your current account password",
+      "account_password": "Account Password",
+      "enter_password_error": "Please enter password",
+      "user_verification_error": "Unable to verify user, please log in again",
+      "incorrect_password": "Incorrect password, please try again later",
+      "password_mismatch": "New passwords do not match",
+      "enter_new_password": "Please enter new password",
+      "change_password_failed": "Failed to change password, please try again later",
+      "general_error": "An error occurred, please try again later",
+      "verifying": "Verifying...",
+      "verified": "Verified",
+      "new_password": "New Password",
+      "confirm_password": "Confirm Password",
+      "changing": "Changing..."
+    },
+    "change_name": {
+      "change_nickname_title": "Change Nickname",
+      "enter_new_name": "Please enter new name",
+      "enter_new_nickname_error": "Please enter a new nickname",
+      "no_token_error": "No valid login token found, please log in again",
+      "update_failed": "Failed to update nickname, please try again later",
+      "general_error": "An error occurred, please try again later",
+      "changing": "Changing..."
+    },
+    "change_gender": {
+      "change_gender_title": "Change Gender",
+      "select_new_gender": "Please select new gender",
+      "gender": "Gender",
+      "male": "Male",
+      "female": "Female",
+      "select_gender_error": "Please select gender",
+      "no_token_error": "No valid login token found, please log in again",
+      "update_failed": "Failed to update gender, please try again later",
+      "general_error": "An error occurred, please try again later",
+      "changing": "Changing..."
+    },
+    "change_car": {
+      "change_license_plate_title": "Change License Plate",
+      "enter_new_license_plate": "Please enter new license plate",
+      "enter_new_license_plate_placeholder": "Please enter new license plate",
+      "enter_license_plate_error": "Please enter license plate number",
+      "license_plate_min_length": "License plate number must be at least 2 characters",
+      "license_plate_max_length": "License plate number cannot exceed 10 characters",
+      "license_plate_invalid_format": "License plate number can only contain letters, numbers, and spaces",
+      "save_success": "License plate number saved successfully",
+      "save_failed": "Unable to save license plate number",
+      "temporary_save_failed": "Temporarily unable to save license plate number",
+      "changing": "Changing..."
     }
   },
   "auth": {

+ 159 - 2
i18n/locales/zh-TW/translation.json

@@ -1,4 +1,9 @@
 {
+  "tabs": {
+    "home": "主頁",
+    "charging": "充電",
+    "account": "帳戶"
+  },
   "login": {
     "tip1": "提示: 舊用戶",
     "tip2": "必須",
@@ -129,7 +134,8 @@
     "next": "下一步 >",
     "confirm": "確認",
     "cancel": "取消",
-    "title": ""
+    "title": "",
+    "agree": "同意並繼續"
   },
   "home": {
     "vehicle_info_error_title": "無法檢測車輛資訊",
@@ -219,7 +225,8 @@
       "cancel": "取消"
     },
     "coupon": {
-      "valid_until": "有效期至"
+      "valid_until": "有效期至",
+      "noCoupon": "暫時戶口沒有優惠券。"
     },
     "error_fetching_coupons": "Error fetching coupons:",
     "payment_timeout_title": "Payment Timeout",
@@ -274,11 +281,161 @@
     "no_transactions_message": "您需要至少消費一次才可享用VIP專屬優惠!",
     "error_fetching_transactions": "Error fetching transaction records:"
   },
+  "penalty_payment": {
+    "unpaid_penalty_title": "尚未繳付罰款的充電記錄",
+    "actual_end_time": "實際充電到期時間",
+    "actual_charging_end_time": "實際充電結束時間",
+    "charging_date": "充電日期",
+    "charging_location": "充電地點",
+    "penalty_amount": "罰款金額",
+    "order_number": "訂單編號",
+    "pay_penalty": "支付罰款",
+    "payment_success": "支付成功",
+    "penalty_paid": "罰款已成功支付",
+    "payment_failed": "支付失敗",
+    "insufficient_balance": "餘額不足,即將跳轉到錢包",
+    "payment_error": "發生錯誤,請稍後再試",
+    "payment_confirmation": "將會從錢包餘額扣除款項以繳交罰款"
+  },
   "charging": {
     "no_ongoing": {
       "title": "暫無正在進行的充電",
       "subtitle": "立刻前往Crazy Charge 充電站充電吧!",
       "available_connectors": "現時可用充電槍數目"
+    },
+    "result_detail_page": {
+      "time_period": "時段",
+      "price_per_kwh": "價格(/度)",
+      "unable_open_maps": "無法打開Google地圖。請確保您的設備上已安裝。",
+      "route": "路線",
+      "charging_fee": "收費",
+      "per_kwh": "每度電",
+      "available_connectors": "現時可用充電槍數目",
+      "loading": "Loading...",
+      "station_info": "充電站資訊",
+      "pricing_details": "收費詳情",
+      "others": "其他"
+    }
+  },
+  "account": {
+    "wallet": "錢包",
+    "my_vehicle": "我的車輛",
+    "charging_history": "充電記錄",
+    "account_management": "帳戶管理",
+    "allow_notifications": "允許通知",
+    "dark_mode": "深色模式",
+    "user_terms": "用戶條款",
+    "logout": "登出",
+    "notification_settings": "通知設定",
+    "notification_settings_message": "要更改通知設定,請前往手機的設定頁面,允許CrazyCharge通知。",
+    "go_to_settings": "前往設定",
+    "notification_update_failed": "更新通知權限失敗"
+  },
+  "chargingHistory": {
+    "charging_records": "充電記錄",
+    "charging_record": {
+      "day": "日",
+      "to": "至",
+      "charged_amount": "已充電度數",
+      "amount_payable": "應付金額",
+      "order_in_progress": "訂單進行中",
+      "order_details": "訂單詳情"
+    },
+    "charging_details": {
+      "invalid_time": "Invalid Time",
+      "order_number": "訂單编號",
+      "charging_time": "充電時間",
+      "charging_station": "充電站位置",
+      "amount_paid": "實付",
+      "coupon_payment": "優惠券支付",
+      "peak_electricity": "峰時總電量",
+      "peak_rate": "峰時電價",
+      "peak_fee": "峰時總電費",
+      "flat_electricity": "平時總電量",
+      "flat_rate": "平時電價",
+      "flat_fee": "平時總電費",
+      "valley_electricity": "穀時總電量",
+      "valley_rate": "穀時電價",
+      "valley_fee": "穀時總電費",
+      "total_electricity": "總電量",
+      "electricity_rate": "電價",
+      "total_fee": "總電費"
+    }
+  },
+  "accountSettings": {
+    "account_management": "帳戶管理",
+    "email": "電郵地址",
+    "password": "帳戶密碼",
+    "nickname": "暱稱",
+    "gender": "性別",
+    "license_plate": "車牌號碼",
+    "phone": "電話號碼",
+    "no_phone": "No phone number provided",
+    "delete_account": "刪除帳戶",
+    "delete_account_confirmation": "確定要刪除帳戶嗎?此操作無法撤銷。",
+    "delete": "刪除",
+    "delete_account_error": "刪除帳戶失敗,請重試。",
+    "male": "男",
+    "female": "女",
+    "change_email": {
+      "change_email_title": "更改郵箱",
+      "enter_new_email": "請輸入新郵箱",
+      "enter_new_email_error": "請輸入新的郵箱",
+      "no_token_error": "未找到有效的登錄令牌,請重新登錄",
+      "update_failed": "更新郵箱失敗,請稍後再試",
+      "general_error": "發生錯誤,請稍後再試",
+      "changing": "更改中..."
+    },
+    "change_password": {
+      "change_password_title": "更改密碼",
+      "enter_current_password": "請輸入您現時的帳戶密碼",
+      "account_password": "帳戶密碼",
+      "enter_password_error": "請輸入密碼",
+      "user_verification_error": "無法驗證用戶,請重新登錄",
+      "incorrect_password": "密碼錯誤,請稍後再試",
+      "password_mismatch": "新密碼不相符",
+      "enter_new_password": "請輸入新密碼",
+      "change_password_failed": "密碼更改失敗,請稍後再試",
+      "general_error": "發生錯誤,請稍後再試",
+      "verifying": "驗證中...",
+      "verified": "已驗證",
+      "new_password": "新密碼",
+      "confirm_password": "確認密碼",
+      "changing": "更改中..."
+    },
+    "change_name": {
+      "change_nickname_title": "更改暱稱",
+      "enter_new_name": "請輸入新名稱",
+      "enter_new_nickname_error": "請輸入新的暱稱",
+      "no_token_error": "未找到有效的登錄令牌,請重新登錄",
+      "update_failed": "更新暱稱失敗,請稍後再試",
+      "general_error": "發生錯誤,請稍後再試",
+      "changing": "更改中..."
+    },
+    "change_gender": {
+      "change_gender_title": "更改性別",
+      "select_new_gender": "請選擇新性別",
+      "gender": "性別",
+      "male": "男",
+      "female": "女",
+      "select_gender_error": "請選擇性別",
+      "no_token_error": "未找到有效的登錄令牌,請重新登錄",
+      "update_failed": "更新性別失敗,請稍後再試",
+      "general_error": "發生錯誤,請稍後再試",
+      "changing": "更改中..."
+    },
+    "change_car": {
+      "change_license_plate_title": "更改車牌號碼",
+      "enter_new_license_plate": "請輸入新車牌號碼",
+      "enter_new_license_plate_placeholder": "請輸入新車牌號碼",
+      "enter_license_plate_error": "請輸入車牌號碼",
+      "license_plate_min_length": "車牌號碼至少需要2個字符",
+      "license_plate_max_length": "車牌號碼不能超過10個字符",
+      "license_plate_invalid_format": "車牌號碼只能包含字母、數字和空格",
+      "save_success": "車牌號碼保存成功",
+      "save_failed": "無法保存車牌號碼",
+      "temporary_save_failed": "暫時無法保存車牌號碼",
+      "changing": "更改中..."
     }
   },
   "auth": {