Browse Source

feat: 继续页面国际化编译

kuns 2 weeks ago
parent
commit
a10e9174a0

+ 1 - 1
.env.development

@@ -1,2 +1,2 @@
 EXPO_PUBLIC_NODE_ENV=development
-EXPO_PUBLIC_API_URL=http://47.115.173.98:12000/api/v1
+EXPO_PUBLIC_API_URL=https://api.crazycharge.com.hk/api/v1

+ 39 - 27
app/(auth)/(tabs)/(home)/optionPage.tsx

@@ -1,3 +1,4 @@
+// app/(auth)/(tabs)/(home)/optionPage.tsx
 import {
     View,
     Text,
@@ -12,12 +13,14 @@ import {
 import React, { useCallback, useEffect, useState } from 'react';
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { router, useFocusEffect, useNavigation } from 'expo-router';
-import { CrossLogoSvg, PreviousPageBlackSvg, RightArrowIconSvg } from '../../../../component/global/SVG';
+import { PreviousPageBlackSvg } from '../../../../component/global/SVG';
 import { useChargingStore } from '../../../../providers/scan_qr_payload_store';
 import { walletService } from '../../../../service/walletService';
 import { chargeStationService } from '../../../../service/chargeStationService';
+import { useTranslation } from '../../../../util/hooks/useTranslation';
 
 const optionPage = () => {
+    const { t } = useTranslation();
     const {
         total_power,
         setTotalPower,
@@ -32,28 +35,28 @@ const optionPage = () => {
     } = useChargingStore();
     const buttonText = [
         {
-            power: '20度電',
-            minute: '25分鐘',
+            power: t('chargingOption.option.kwh_20'),
+            minute: t('chargingOption.option.min_25'),
             total_power: 20
         },
         {
-            power: '25度電',
-            minute: '30分鐘',
+            power: t('chargingOption.option.kwh_25'),
+            minute: t('chargingOption.option.min_30'),
             total_power: 25
         },
         {
-            power: '30度電',
-            minute: '40分鐘',
+            power: t('chargingOption.option.kwh_30'),
+            minute: t('chargingOption.option.min_40'),
             total_power: 30
         },
         {
-            power: '40度電',
-            minute: '45分鐘',
+            power: t('chargingOption.option.kwh_40'),
+            minute: t('chargingOption.option.min_45'),
             total_power: 40
         },
         {
-            power: '充滿停機',
-            minute: '最多80度電',
+            power: t('chargingOption.option.full_charge'),
+            minute: t('chargingOption.option.up_to_80kwh'),
             total_power: 80
         }
     ];
@@ -117,7 +120,11 @@ const optionPage = () => {
     );
 
     const displayedText =
-        promotion_code.length > 0 ? `$ ${sum_of_coupon}劵` : useableCoupon.length > 0 ? '有可用劵' : '無可用劵';
+        promotion_code.length > 0 
+            ? t('chargingOption.option.coupon_value', { value: sum_of_coupon }) 
+            : useableCoupon.length > 0 
+                ? t('chargingOption.option.available_coupon') 
+                : t('chargingOption.option.no_coupon');
 
     return (
         <SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
@@ -140,8 +147,7 @@ const optionPage = () => {
                 <View className="flex-1 flex-col justify-center items-center">
                     {/* top word and logo */}
                     <View className="flex-col justify-center items-center gap-2">
-                        {/* <Text className="text-2xl"> */}
-                        <Text className="text-2xl font-[600]">選擇充電度數</Text>
+                        <Text className="text-2xl font-[600]">{t('chargingOption.option.select_charge_amount')}</Text>
                         <Image
                             source={require('../../../../assets/battery.png')}
                             style={{ width: screenWidth * 0.3, height: undefined, aspectRatio: 1 }}
@@ -153,7 +159,7 @@ const optionPage = () => {
                       <View className="flex-row flex-wrap justify-center items-center gap-4 ">
                           {/* not showing full power because it needs to be full width */}
                           {buttonText
-                              .filter((item) => item.power != '充滿停機')
+                              .filter((item) => item.power != t('chargingOption.option.full_charge'))
                               .map((buttonTextObj: any, index) => (
                                   <Pressable
                                       key={index}
@@ -176,7 +182,7 @@ const optionPage = () => {
                               ))}
                           {/* this is 充滿停機only */}
                           {buttonText
-                              .filter((item) => item.power === '充滿停機')
+                              .filter((item) => item.power === t('chargingOption.option.full_charge'))
                               .map((item, index) => (
                                   <Pressable
                                       key={index}
@@ -202,16 +208,18 @@ const optionPage = () => {
                             onPress={() => {
                                 if (!total_power) {
                                     Alert.alert(
-                                        '選擇充電方案',
-                                        '請先選擇充電方案再選擇優惠券',
-                                        [{ text: '確定', style: 'default' }],
+                                        t('chargingOption.option.alert.select_plan_title'),
+                                        t('chargingOption.option.alert.select_plan_message'),
+                                        [{ text: t('common.confirm'), style: 'default' }],
                                         {
                                             cancelable: true
                                         }
                                     );
                                 } else {
-                                    Alert.alert('提醒您', '使用優惠券的交易每度電以正價$3.5元計算!', [
-                                        { text: '我知道了', onPress: () => router.push('/selectCoupon') }
+                                    Alert.alert(
+                                        t('chargingOption.option.alert.reminder_title'), 
+                                        t('chargingOption.option.alert.reminder_message'), [
+                                        { text: t('common.confirm'), onPress: () => router.push('/selectCoupon') }
                                     ]);
                                 }
                             }}
@@ -248,15 +256,17 @@ const optionPage = () => {
                             }}
                             className="w-[45%] border border-[#02677D] p-1 md:p-2 lg:p-3 xl:p-4 py-4 lg:py-6 rounded-xl "
                         >
-                            <Text className="text-[#02677D] text-base lg:text-lg xl:text-xl text-center">取消</Text>
+                            <Text className="text-[#02677D] text-base lg:text-lg xl:text-xl text-center">
+                                {t('common.cancel')}
+                            </Text>
                         </Pressable>
                         <Pressable
                             onPress={() => {
                                 if (!total_power) {
                                     Alert.alert(
-                                        '選擇充電方案',
-                                        '請先選擇充電方案再確認',
-                                        [{ text: '確定', style: 'default' }],
+                                        t('chargingOption.option.alert.select_plan_title'),
+                                        t('chargingOption.option.alert.confirm_plan_message'),
+                                        [{ text: t('common.confirm'), style: 'default' }],
                                         {
                                             cancelable: true
                                         }
@@ -267,7 +277,9 @@ const optionPage = () => {
                             }}
                             className="w-[45%] border border-[#02677D] p-1 md:p-2 lg:p-3 xl:p-4 py-4 lg:py-6 rounded-xl "
                         >
-                            <Text className="text-[#02677D] text-base lg:text-lg xl:text-xl text-center">確認</Text>
+                            <Text className="text-[#02677D] text-base lg:text-lg xl:text-xl text-center">
+                                {t('common.confirm')}
+                            </Text>
                         </Pressable>
                     </View>
                 </View>
@@ -276,4 +288,4 @@ const optionPage = () => {
     );
 };
 
-export default optionPage;
+export default optionPage;

+ 23 - 25
app/(auth)/(tabs)/(home)/selectCoupon.tsx

@@ -1,3 +1,4 @@
+// app/(auth)/(tabs)/(home)/selectCoupon.tsx
 import {
     Image,
     View,
@@ -8,9 +9,7 @@ import {
     Modal,
     Animated,
     ScrollView,
-    Button,
-    BackHandler,
-    Alert
+    BackHandler
 } from 'react-native';
 
 import { SafeAreaView } from 'react-native-safe-area-context';
@@ -22,9 +21,11 @@ import { useChargingStore } from '../../../../providers/scan_qr_payload_store';
 import { ArrowRightSvg } from '../../../../component/global/SVG';
 import { useRef } from 'react';
 import NormalButton from '../../../../component/global/normal_button';
+import { useTranslation } from '../../../../util/hooks/useTranslation';
 
 //this is from optionPage => 優惠券
 const SelectCouponComponent = () => {
+    const { t } = useTranslation();
     const screenHeight = Dimensions.get('window').height;
     const {
         promotion_code,
@@ -77,7 +78,7 @@ const SelectCouponComponent = () => {
         const skimmedDownArray = coupon_details_array?.map((couponDetailObj: any) => ({
             amount: couponDetailObj.coupon.amount,
             id: couponDetailObj.id,
-            expire_date: couponDetailObj.expire_date || '永久'
+            expire_date: couponDetailObj.expire_date || t('common.permanent')
         }));
 
         const totalCouponAmount = skimmedDownArray.reduce((acc: number, coupon: any) => acc + coupon.amount, 0);
@@ -145,10 +146,10 @@ const SelectCouponComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginVertical: 25 }}>優惠券</Text>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('selectCoupons.coupons.title')}</Text>
                 </View>
                 <View className="flex-1">
-                    <CouponTabViewComponent titles={['可用優惠券', '已使用/失效']} />
+                    <CouponTabViewComponent titles={[t('selectCoupons.coupons.available'), t('selectCoupons.coupons.used_expired')]} />
                     {promotion_code.length > 0 && (
                         <View
                             style={{
@@ -169,7 +170,7 @@ const SelectCouponComponent = () => {
                                 onPress={showBottomSheet}
                             >
                                 <Text className="text-[#02677D] text-2xl lg:text-4xl text-center py-1  pr-4 lg:pr-6 font-[600] lg:py-4">
-                                    馬上使用
+                                    {t('common.use_now')}
                                 </Text>
                                 <View className="flex mb-2 bg-[#02677D] rounded-full items-center justify-center w-10 h-10 relative">
                                     <ArrowRightSvg />
@@ -205,7 +206,7 @@ const SelectCouponComponent = () => {
                             className="flex-1 flex-col bg-white p-8 "
                             contentContainerStyle={{ paddingBottom: 100 }}
                         >
-                            <Text className="text-lg md:text-xl lg:text-2xl">優惠券細節</Text>
+                            <Text className="text-lg md:text-xl lg:text-2xl">{t('selectCoupons.coupon_details')}</Text>
                             <View style={{ height: 1, backgroundColor: '#ccc', marginVertical: 24 }} />
                             {/* coupon row */}
                             {processedCoupons &&
@@ -224,12 +225,14 @@ const SelectCouponComponent = () => {
                                                 className="flex flex-col ml-2 lg:ml-4 "
                                             >
                                                 <Text className="text-base lg:text-xl ">
-                                                    ${couponObj.coupon_detail.amount} 現金劵
+                                                    ${couponObj.coupon_detail.amount} {t('selectCoupons.coupons.cash_voucher')}
                                                 </Text>
                                                 <Text className=" text-sm lg:text-base my-1 lg:mt-2 lg:mb-4 ">
-                                                    有效期{'  '}
+                                                    {t('selectCoupons.coupons.valid_until')}{'  '}
                                                     <Text className="font-[500] text-[#02677D]">
-                                                        至 {couponObj.coupon_detail.expire_date.slice(0, 10)}
+                                                        {couponObj.coupon_detail.expire_date === t('common.permanent') 
+                                                            ? t('common.permanent') 
+                                                            : t('common.to_date', { date: couponObj.coupon_detail.expire_date.slice(0, 10) })}
                                                     </Text>
                                                 </Text>
                                             </View>
@@ -249,7 +252,7 @@ const SelectCouponComponent = () => {
                             <View style={{ height: 1, backgroundColor: '#ccc', marginVertical: 12 }} />
                             {/* 服務條款 */}
                             <NormalButton
-                                title={<Text className="text-white text-sm lg:text-lg">立即使用</Text>}
+                                title={<Text className="text-white text-sm lg:text-lg">{t('common.use_now')}</Text>}
                                 onPress={() => {
                                     setIsBottomSheetVisible(false);
                                     router.push('/totalPayment');
@@ -259,31 +262,26 @@ const SelectCouponComponent = () => {
                             <View>
                                 <View className="">
                                     <Text className="text-base md:text-lg lg:text-xl pb-2 lg:pb-3 xl:pb-4">
-                                        服務條款與細則
+                                        {t('selectCoupons.coupon.terms_and_conditions')}
                                     </Text>
                                     <View className="flex flex-col items-center space-y-2">
                                         <Text className="text-xs md:text-sm font-[300]">
-                                            ・ 此券持有人可在本券有效期內於任何位於Crazy Charge
-                                            之香港分店換取同等價值充電服務,逾期無效。
+                                            {t('selectCoupons.coupon.term1')}
                                         </Text>
                                         <Text className="text-xs lg:text-sm  font-[300]">
-                                            ・
-                                            此優惠券使用時,電費將以正常價格$3.5元/每度電計算,不適用於貓頭鷹時段或其他折扣時段的電力價格計算。
+                                            {t('selectCoupons.coupon.term2')}
                                         </Text>
                                         <Text className="text-xs lg:text-sm  font-[300]">
-                                            ・ 此券不能用以套换現金或其他面值之現金券,持有人不獲現金或其他形式之找贖。
+                                            {t('selectCoupons.coupon.term3')}
                                         </Text>
                                         <Text className="text-xs lg:text-sm  font-[300]">
-                                            ・ 使用者一旦在本 APP
-                                            內確認使用電子優惠券,即視為同意依優惠券規則進行消費抵扣,相關優惠券將立即從帳戶中扣除,且扣除後不得退還。
+                                            {t('selectCoupons.coupon.term4')}
                                         </Text>
                                         <Text className="text-xs lg:text-sm  font-[300]">
-                                            ・
-                                            即便實際充電消費金額未達到電子優惠券的面額,亦不會就差額部分進行退款。優惠券的使用旨在為用戶提供充電優惠,而非現金兌換或退款工具。
+                                            {t('selectCoupons.coupon.term5')}
                                         </Text>
                                         <Text className="text-xs lg:text-sm  font-[300]">
-                                            ・如有任何爭議,Crazy Charge
-                                            保留更改有關使用此現金券之條款及細則,而毋須另行通知。
+                                            {t('selectCoupons.coupon.term6')}
                                         </Text>
                                     </View>
                                 </View>
@@ -306,4 +304,4 @@ const styles = StyleSheet.create({
     }
 });
 
-export default SelectCouponComponent;
+export default SelectCouponComponent;

+ 60 - 56
app/(auth)/(tabs)/(home)/totalPayment.tsx

@@ -1,3 +1,4 @@
+// app/(auth)/(tabs)/(home)/totalPayment.tsx
 import { router, useFocusEffect, useNavigation } from 'expo-router';
 import {
     View,
@@ -25,7 +26,10 @@ import axios from 'axios';
 import sha256 from 'crypto-js/sha256';
 import { walletService } from '../../../../service/walletService';
 import AsyncStorage from '@react-native-async-storage/async-storage';
+import { useTranslation } from '../../../../util/hooks/useTranslation';
+
 const TotalPayment = () => {
+    const { t } = useTranslation();
     const {
         promotion_code,
         stationID,
@@ -79,9 +83,9 @@ const TotalPayment = () => {
                 // More specific error handling
                 if (axios.isAxiosError(error)) {
                     const errorMessage = error.response?.data?.message || 'Network error occurred';
-                    Alert.alert('Error', `Unable to fetch price: ${errorMessage}`, [
+                    Alert.alert(t('common.error'), `${t('wallet.error_fetching_balance')}: ${errorMessage}`, [
                         {
-                            text: 'OK',
+                            text: t('common.ok'),
                             onPress: () => {
                                 cleanupData();
                                 router.push('/mainPage');
@@ -89,9 +93,9 @@ const TotalPayment = () => {
                         }
                     ]);
                 } else {
-                    Alert.alert('Error', 'An unexpected error occurred while fetching the price', [
+                    Alert.alert(t('common.error'), t('wallet.error_fetching_balance'), [
                         {
-                            text: 'OK',
+                            text: t('common.ok'),
                             onPress: () => {
                                 cleanupData();
                                 router.push('/mainPage');
@@ -142,8 +146,8 @@ const TotalPayment = () => {
                     setOutTradeNo('');
                     paymentInitiatedTime.current = null;
                     Alert.alert(
-                        'Payment Timeout',
-                        'The payment status check has timed out. Please check your payment history.'
+                        t('payment.payment_timeout_title'),
+                        t('payment.payment_timeout_message')
                     );
                 }
             }
@@ -166,14 +170,14 @@ const TotalPayment = () => {
             // console.log('outTradeNo in scanQR Page checkpaymentstatus ', outTradeNo);
             const result = await walletService.checkPaymentStatus(outTradeNo);
             setPaymentStatus(result);
-            // console.log('checkPaymentStatus from scan QR checkpaymentStatus', result);
+            // console.log('checkPaymentStatus from scan QR checkpaymentstatus', result);
 
-            if (result && !result.some((item) => item.errmsg?.includes('處理中'))) {
+            if (result && !result.some((item) => item.errmsg?.includes(t('payment.processing')))) {
                 // Payment successful
                 // console.log('totalFee', totalFee);
-                Alert.alert('付款已成功', `你已成功增值。請重新掃描去啟動充電槍。`, [
+                Alert.alert(t('payment.payment_success_title'), t('payment.payment_success_message', { amount: totalPrice }), [
                     {
-                        text: '確認',
+                        text: t('payment.confirm'),
                         onPress: async () => {
                             cleanupData();
                             router.push('/mainPage');
@@ -181,9 +185,9 @@ const TotalPayment = () => {
                     }
                 ]);
             } else {
-                Alert.alert('付款失敗', '請再試一次。', [
+                Alert.alert(t('payment.payment_failed_title'), t('payment.payment_failed_message'), [
                     {
-                        text: '確定',
+                        text: t('payment.ok'),
                         onPress: () => {
                             cleanupData();
                             router.push('/mainPage');
@@ -196,7 +200,7 @@ const TotalPayment = () => {
             paymentInitiatedTime.current = null;
         } catch (error) {
             console.error('Failed to check payment status:', error);
-            Alert.alert('Error', 'Failed to check payment status. Please check your payment history.');
+            Alert.alert(t('common.error'), t('payment.payment_status_check_failed'));
         }
     };
     const showLoadingAndNavigate = async () => {
@@ -222,31 +226,31 @@ const TotalPayment = () => {
             let car, user_id, walletBalance, price_for_pay;
             setLoading(true);
             if (currentPriceTotalPayment === null) {
-                Alert.alert('Please wait', 'Still loading price information...');
+                Alert.alert(t('payment.processing'), t('payment.wait_loading_price'));
                 return;
             }
             //fetch car with proper try catch
             try {
                 car = await chargeStationService.getUserDefaultCars();
                 if (!car?.data?.id) {
-                    Alert.alert('Failed to fetch UDCC', 'Please try again later');
+                    Alert.alert(t('payment.error_title'), t('payment.failed_fetch_udcc'));
                     return;
                 }
             } catch (error) {
                 console.error('Failed to fetch user default car:', error);
-                Alert.alert('Failed to fetch UDC', 'Please try again later');
+                Alert.alert(t('payment.error_title'), t('payment.failed_fetch_udc'));
                 return;
             }
             //fetch user id with proper try catch
             try {
                 user_id = await authenticationService.getUserInfo();
                 if (!user_id?.data?.id) {
-                    Alert.alert('Failed to fetch userID', 'Please try again later');
+                    Alert.alert(t('payment.error_title'), t('payment.failed_fetch_userid'));
                     return;
                 }
             } catch (error) {
                 console.error('Failed to fetch user ID:', error);
-                Alert.alert('Failed to fetch user ID', 'Please try again later');
+                Alert.alert(t('payment.error_title'), t('payment.failed_fetch_userid'));
                 return;
             }
 
@@ -255,7 +259,7 @@ const TotalPayment = () => {
                 walletBalance = await walletService.getWalletBalance();
             } catch (error) {
                 console.error('Failed to fetch user wallet:', error);
-                Alert.alert('Failed to fetch user wallet', 'Please try again later');
+                Alert.alert(t('payment.error_title'), t('payment.failed_fetch_wallet'));
                 return;
             }
 
@@ -276,11 +280,11 @@ const TotalPayment = () => {
 
                     if (unpaidPenalties.length > 0) {
                         Alert.alert(
-                            '未付罰款',
-                            '您有未支付的罰款。請先支付罰款後再重新掃描充電。',
+                            t('payment.unpaid_penalty_title'),
+                            t('payment.unpaid_penalty_message'),
                             [
                                 {
-                                    text: '查看詳情',
+                                    text: t('payment.view_details'),
                                     onPress: () => {
                                         // Navigate to a page showing penalty details
                                         cleanupData();
@@ -298,7 +302,7 @@ const TotalPayment = () => {
                                     }
                                 },
                                 {
-                                    text: '返回',
+                                    text: t('scanQr.back'),
                                     onPress: () => {
                                         cleanupData();
                                         if (router.canGoBack()) {
@@ -315,7 +319,7 @@ const TotalPayment = () => {
                     }
                 }
             } catch (error) {
-                Alert.alert('Error', 'Failed to fetch reservation histories for penalty checking purpose');
+                Alert.alert(t('common.error'), t('scanQr.failed_fetch_reservations'));
             }
 
             const now = new Date();
@@ -347,9 +351,9 @@ const TotalPayment = () => {
 
             // check if user has enough wallet, if not, link to qf pay page
             if (totalPrice === null) {
-                Alert.alert('Error', 'Unable to fetch totalPrice', [
+                Alert.alert(t('common.error'), t('scanQr.failed_fetch_totalprice'), [
                     {
-                        text: 'OK',
+                        text: t('common.ok'),
                         onPress: () => {
                             cleanupData();
                             router.push('/mainPage');
@@ -383,9 +387,9 @@ const TotalPayment = () => {
                     if (response.error) {
                         console.log('Error1', response.error);
                         // Handle error response from the service
-                        Alert.alert('掃描失敗 請稍後再試。', response.message || '未知錯誤', [
+                        Alert.alert(t('payment.scan_failed'), response.message || t('payment.unknown_error'), [
                             {
-                                text: '返回主頁',
+                                text: t('payment.back_main'),
                                 onPress: () => {
                                     cleanupData();
                                     router.push('/mainPage');
@@ -396,18 +400,18 @@ const TotalPayment = () => {
                     }
 
                     if (response === 200 || response === 201) {
-                        Alert.alert('啟動成功', '請按下確認並等待頁面稍後自動跳轉至充電介面', [
+                        Alert.alert(t('payment.charging_started_title'), t('payment.charging_started_message'), [
                             {
-                                text: '確認',
+                                text: t('payment.confirm'),
                                 onPress: showLoadingAndNavigate
                             }
                         ]);
                     } else {
                         console.log('Error111', response, payloadForPay.connector);
 
-                        Alert.alert('掃描失敗 請稍後再試。', response.error_msg || '未知錯誤', [
+                        Alert.alert(t('payment.scan_failed'), response.error_msg || t('payment.unknown_error'), [
                             {
-                                text: '返回主頁',
+                                text: t('payment.back_main'),
                                 onPress: () => {
                                     cleanupData();
                                     router.push('/mainPage');
@@ -417,9 +421,9 @@ const TotalPayment = () => {
                     }
                 } catch (error) {
                     console.error('Payment submission failed:', error);
-                    Alert.alert('錯誤', '付款提交失敗,請稍後再試。', [
+                    Alert.alert(t('common.error'), t('payment.payment_submit_failed'), [
                         {
-                            text: 'OK',
+                            text: t('common.ok'),
                             onPress: () => {
                                 cleanupData();
                                 router.push('/mainPage');
@@ -467,7 +471,7 @@ const TotalPayment = () => {
                 const obj = {
                     // appcode: '6937EF25DF6D4FA78BB2285441BC05E9',
                     appcode: '636E234FB30D43598FC8F0140A1A7282',
-                    goods_name: 'Crazy Charge 錢包增值',
+                    goods_name: t('payment.wallet_top_up'),
                     out_trade_no: response,
                     paysource: 'crazycharge_checkout',
                     return_url: 'https://crazycharge.com.hk/completed',
@@ -498,26 +502,26 @@ const TotalPayment = () => {
                 try {
                     const supported = await Linking.canOpenURL(url);
                     if (supported) {
-                        Alert.alert('', '偵測到您錢包餘額不足,現在為您跳轉到充值頁面', [
+                        Alert.alert('', t('payment.insufficient_balance_redirect'), [
                             {
-                                text: '確定',
+                                text: t('payment.ok'),
                                 onPress: async () => {
                                     await Linking.openURL(url);
                                 }
                             }
                         ]);
                     } else {
-                        Alert.alert('錯誤', '請稍後再試');
+                        Alert.alert(t('common.error'), t('payment.try_again_later'));
                     }
                 } catch (error) {
                     console.error('Top-up failed:', error);
-                    Alert.alert('Error', '一次性付款失敗,請稍後再試');
+                    Alert.alert(t('common.error'), t('payment.one_time_payment_failed'));
                 }
             } else {
-                Alert.alert('Error', 'failed to fetch outTradeNo.');
+                Alert.alert(t('common.error'), t('payment.failed_fetch_outtrade'));
             }
         } catch (error) {
-            Alert.alert('錯誤', '一次性付款失敗,請稍後再試');
+            Alert.alert(t('common.error'), t('payment.one_time_payment_failed'));
         }
     };
 
@@ -527,7 +531,7 @@ const TotalPayment = () => {
                 <View className="flex-1 justify-center items-center bg-black/50">
                     <View className="bg-white p-6 rounded-lg items-center">
                         <ActivityIndicator size="large" color="#02677D" />
-                        <Text className="mt-3">請稍候...</Text>
+                        <Text className="mt-3">{t('common.please_wait')}</Text>
                     </View>
                 </View>
             </Modal>
@@ -547,29 +551,29 @@ const TotalPayment = () => {
                     </Pressable>
                 </View>
                 <View style={{ marginTop: 25 }}>
-                    <Text style={{ fontSize: 45, paddingBottom: 12 }}>付款概要</Text>
+                    <Text style={{ fontSize: 45, paddingBottom: 12 }}>{t('payment.summary_title')}</Text>
 
                     <View>
                         <View className="flex-row justify-between">
-                            <Text className="text-base lg:text-lg ">充電費用</Text>
+                            <Text className="text-base lg:text-lg ">{t('payment.charging_fee')}</Text>
                             <Text className="text-base lg:text-lg">
-                                HK $ {currentPriceTotalPayment ? currentPriceTotalPayment * total_power : 'Loading...'}
+                                HK $ {currentPriceTotalPayment ? currentPriceTotalPayment * total_power : t('common.loading')}
                             </Text>
                         </View>
 
                         <Text style={styles.grayColor} className="text-sm lg:text-base mt-4">
-                            結算電度數 : {total_power == 80 ? '充滿停機' : `${total_power} KWh`}
+                            {t('payment.settled_kwh')}: {total_power == 80 ? t('payment.full_charge') : `${total_power} KWh`}
                         </Text>
                         <Text style={styles.grayColor} className="text-sm lg:text-base mt-4">
-                            每度電價錢 : $ {currentPriceTotalPayment ? currentPriceTotalPayment : 'Loading...'}
+                            {t('payment.price_per_kwh')}: $ {currentPriceTotalPayment ? currentPriceTotalPayment : t('common.loading')}
                         </Text>
                         <Text style={styles.grayColor} className="text-sm lg:text-base mt-4">
-                          注: 跨時段充電將按不同時段的單價分別計费,實際充電量可能浮動
+                          {t('payment.note')}
                         </Text>
                         <View className="h-0.5 my-3 bg-[#f4f4f4]" />
 
                         {processed_coupon_store && processed_coupon_store.length > 0 && (
-                            <Text className="text-base lg:text-lg mb-4 lg:mb-6">優惠劵</Text>
+                            <Text className="text-base lg:text-lg mb-4 lg:mb-6">{t('payment.coupon')}</Text>
                         )}
                         {processed_coupon_store &&
                             processed_coupon_store?.map((couponObj: any) => (
@@ -584,12 +588,12 @@ const TotalPayment = () => {
                                         />
                                         <View key={couponObj.coupon_detail.id} className="flex flex-col ml-2 lg:ml-4 ">
                                             <Text className="text-base lg:text-xl text-[#888888] ">
-                                                ${couponObj.coupon_detail.amount} 現金劵
+                                                ${couponObj.coupon_detail.amount} {t('wallet.coupon.cash_voucher')}
                                             </Text>
                                             <Text className=" text-sm lg:text-base my-1 lg:mt-2 lg:mb-4 text-[#888888]">
-                                                有效期{'  '}
+                                                {t('wallet.coupon.valid_until')}{'  '}
                                                 <Text className="font-[500] text-[#02677D]">
-                                                    至 {couponObj.coupon_detail.expire_date.slice(0, 10)}
+                                                    {t('common.to_date', { date: couponObj.coupon_detail.expire_date.slice(0, 10) })}
                                                 </Text>
                                             </Text>
                                         </View>
@@ -611,8 +615,8 @@ const TotalPayment = () => {
                             <View className="h-0.5 my-3 bg-[#f4f4f4]" />
                         )}
                         <View className="flex-row justify-between">
-                            <Text className="text-xl">總計</Text>
-                            <Text className="text-3xl">HK$ {totalPrice !== null ? totalPrice : 'Loading...'}</Text>
+                            <Text className="text-xl">{t('payment.total')}</Text>
+                            <Text className="text-3xl">HK$ {totalPrice !== null ? totalPrice : t('common.loading')}</Text>
                         </View>
                         <View className="mt-4 ">
                             <NormalButton
@@ -624,7 +628,7 @@ const TotalPayment = () => {
                                             fontWeight: '800'
                                         }}
                                     >
-                                        {loading ? '處理中...' : '付款確認'}
+                                        {loading ? t('common.processing') : t('payment.confirm_payment')}
                                     </Text>
                                 }
                                 onPress={handlePay}
@@ -647,4 +651,4 @@ const styles = StyleSheet.create({
     greenColor: {
         color: '#02677D'
     }
-});
+});

+ 8 - 5
component/accountPages/assistancePageComponent.tsx

@@ -1,11 +1,14 @@
+// component/accountPages/assistancePageComponent.tsx
 import { View, Text, ScrollView, Pressable, Linking, Alert } from 'react-native';
-import React, { useContext } from 'react';
+import React from 'react';
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { router } from 'expo-router';
-import { CrossLogoSvg, RightArrowIconSvg } from '../global/SVG';
+import { CrossLogoSvg } from '../global/SVG';
 import { handleGoWhatsApp } from '../../util/index';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const AssistancePageComponent = () => {
+    const { t } = useTranslation();
 
     return (
         <SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
@@ -18,11 +21,11 @@ const AssistancePageComponent = () => {
                     >
                         <CrossLogoSvg />
                     </Pressable>
-                    <Text style={{ fontSize: 45, marginVertical: 25 }}>排除解難</Text>
+                    <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('assistance.troubleshooting')}</Text>
                 </View>
                 <View>
                     <Text className="text-lg">
-                        如果您在使用該應用程式時遇到任何問題,請透過WhatsApp聯絡我們的客戶服務人員:
+                        {t('assistance.description')}
                     </Text>
                     <Pressable onPress={handleGoWhatsApp}>
                         <Text className="text-3xl font-semibold underline text-[#02677D] pt-2">+852 9138 2139</Text>
@@ -33,4 +36,4 @@ const AssistancePageComponent = () => {
     );
 };
 
-export default AssistancePageComponent;
+export default AssistancePageComponent;

+ 15 - 252
component/chargingPage/paymentFinishPageComponent.tsx

@@ -1,3 +1,4 @@
+// component/chargingPage/paymentFinishPageComponent.tsx
 import { View, Text, ScrollView, StyleSheet, Pressable } from 'react-native';
 import React, { useEffect } from 'react';
 import { SafeAreaView } from 'react-native-safe-area-context';
@@ -16,8 +17,10 @@ import {
 } from '../global/SVG';
 import useBookingStore from '../../providers/booking_store';
 import userStore from '../../providers/user_store';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const PaymentFinishPageComponent = () => {
+    const { t } = useTranslation();
     const { bookTime, carID, date, chargingWatt, connectorID, price, stationID, user, paymentFee, carCapacitance } =
         useBookingStore();
     const navigation = useNavigation();
@@ -38,256 +41,27 @@ const PaymentFinishPageComponent = () => {
                 <View style={{ marginTop: 25, flex: 1 }}>
                     <View className="flex-row items-center">
                         <TickLogoSvg />
-                        <Text className="text-3xl pl-2">成功付款</Text>
+                        <Text className="text-3xl pl-2">{t('payment_finish.success_title')}</Text>
                     </View>
-
-                    {/* <View className="items-center pt-4 justify-center">
-                        <SlideInImage source={require('../../assets/car.png')} />
-
-                        <Text className="text-2xl font-light pb-4">
-                            TESLA Model 3
-                        </Text>
-                    </View>
-
-                    <View
-                        style={{ borderWidth: 1 }}
-                        className="border-[#EEEEEE] rounded-2xl"
-                    >
-                        <View className="mx-[5%] flex-column p-4 ">
-                            <View className="flex-row justify-evenly gap-6">
-                                <View className="flex-row items-center space-x-4">
-                                    <BatteryLogoSvg />
-                                    <View className="flex-column ">
-                                        <Text
-                                            className="text-sm"
-                                            style={styles.grayColor}
-                                        >
-                                            已充電
-                                        </Text>
-                                        <Text
-                                            style={styles.greenColor}
-                                            className="text-4xl font-light"
-                                        >
-                                            95%
-                                        </Text>
-                                    </View>
-                                </View>
-                                <View className="flex-row items-center space-x-4">
-                                    <TimeClockLogoSvg />
-
-                                    <View className="flex-column">
-                                        <Text
-                                            className="text-sm"
-                                            style={styles.grayColor}
-                                        >
-                                            充電歷時
-                                        </Text>
-                                        <View className="flex-row items-end">
-                                            <Text
-                                                style={styles.greenColor}
-                                                className="text-4xl font-light"
-                                            >
-                                                39
-                                            </Text>
-                                            <Text
-                                                style={styles.greenColor}
-                                                className="text-sm font-light"
-                                            >
-                                                mins
-                                            </Text>
-                                        </View>
-                                    </View>
-                                </View>
-                            </View>
-                            <View className="h-0.5 w-full my-4 bg-[#EEEEEE]" />
-                            {isMoreInfoButtonPressed ? (
-                                <>
-                                    <View className="h-[100px] flex-row items-center">
-                                        <View className="flex-1 flex-column items-center space-y-2">
-                                            <LightingLogoSvg />
-                                            <Text
-                                                style={styles.grayColor}
-                                                className="text-base"
-                                            >
-                                                充電功率
-                                            </Text>
-                                            <Text
-                                                style={styles.greenColor}
-                                                className="font-bold text-base"
-                                            >
-                                                22.1kW
-                                            </Text>
-                                        </View>
-                                        <View className="flex-1 flex-column items-center space-y-2">
-                                            <BatteryIconSvg />
-                                            <Text
-                                                style={styles.grayColor}
-                                                className="text-base"
-                                            >
-                                                實際功率
-                                            </Text>
-                                            <Text
-                                                style={styles.greenColor}
-                                                className="font-bold text-base"
-                                            >
-                                                30.0kW
-                                            </Text>
-                                        </View>
-                                        <View className="flex-1 flex-column items-center space-y-2">
-                                            <TemperatureIconSvg />
-                                            <Text
-                                                style={styles.grayColor}
-                                                className="text-base"
-                                            >
-                                                溫度
-                                            </Text>
-                                            <Text
-                                                style={styles.greenColor}
-                                                className="font-bold text-base"
-                                            >
-                                                36°c
-                                            </Text>
-                                        </View>
-                                    </View>
-                                    <Pressable
-                                        onPress={() => {
-                                            setIsMoreInfoButtonPressed(
-                                                !isMoreInfoButtonPressed
-                                            );
-                                        }}
-                                        style={{ padding: 2 }}
-                                    >
-                                        <View className="flex-row pt-4 items-center justify-center">
-                                            <Text className="text-sm">
-                                                收起{' '}
-                                            </Text>
-                                            <UpArrowSvg />
-                                        </View>
-                                    </Pressable>
-                                </>
-                            ) : (
-                                <Pressable
-                                    onPress={() => {
-                                        setIsMoreInfoButtonPressed(
-                                            !isMoreInfoButtonPressed
-                                        );
-                                    }}
-                                    style={{ padding: 2 }}
-                                >
-                                    <View className="flex-row items-center justify-center">
-                                        <Text className="text-sm">
-                                            更多資訊{' '}
-                                        </Text>
-                                        <DownArrowSvg />
-                                    </View>
-                                </Pressable>
-                            )}
-                        </View>
-                    </View> */}
-                    {/* 
-                    <View
-                        className="my-4"
-                        style={{
-                            borderWidth: 1,
-                            borderColor: '#EEEEEE',
-                            borderRadius: 12
-                        }}
-                    >
-                        <View className="space-y-3   py-4 mx-[5%]">
-                            <View className="flex-1 flex-row items-center ">
-                                <View className="flex-1 flex-column">
-                                    <Text
-                                        style={styles.grayColor}
-                                        className="text-xs "
-                                    >
-                                        時間日期
-                                    </Text>
-                                    <Text
-                                        style={styles.greenColor}
-                                        className="text-4xl  pt-2 "
-                                    >
-                                        3月14 · 16:15
-                                    </Text>
-                                </View>
-                            </View>
-                            <View className="flex-1 flex-column justify-center">
-                                <Text
-                                    style={styles.grayColor}
-                                    className="text-xs"
-                                >
-                                    充電地點
-                                </Text>
-                                <Text
-                                    style={styles.greenColor}
-                                    className="text-xl"
-                                >
-                                    上環街市充電站
-                                </Text>
-                                <Text
-                                    style={styles.grayColor}
-                                    className="text-base"
-                                >
-                                    香港上環皇后大道中345號
-                                </Text>
-                            </View>
-                            <View className="flex-1 flex-row items-center ">
-                                <View className="flex-column flex-1">
-                                    <Text
-                                        style={styles.grayColor}
-                                        className="text-xs"
-                                    >
-                                        方案
-                                    </Text>
-                                    <Text
-                                        style={styles.greenColor}
-                                        className="text-lg"
-                                    >
-                                        按每度電結算
-                                    </Text>
-                                    <Text
-                                        style={styles.grayColor}
-                                        className="text-sm"
-                                    >
-                                        度數: 50kWh
-                                    </Text>
-                                </View>
-                                <View className="flex-column flex-1">
-                                    <Text
-                                        style={styles.grayColor}
-                                        className="text-xs"
-                                    >
-                                        車輛
-                                    </Text>
-                                    <Text
-                                        style={styles.greenColor}
-                                        className="text-lg"
-                                    >
-                                        TESLA Model 3
-                                    </Text>
-                                </View>
-                            </View>
-                        </View>
-                    </View> */}
-
                     <View>
-                        <Text className="text-xl py-4">收費概要</Text>
+                        <Text className="text-xl py-4">{t('payment_finish.fee_summary')}</Text>
                         <View className="flex-row justify-between">
-                            <Text className="text-base">充電費用</Text>
+                            <Text className="text-base">{t('payment_finish.charging_fee')}</Text>
                             <Text className="text-base">HK$ {paymentFee}</Text>
                         </View>
                         {chargingWatt === '' ? (
                             <Text style={styles.grayColor} className="text-base">
-                                充滿停機預估費用
+                                {t('payment_finish.estimated_full_charge')}
                             </Text>
                         ) : (
                             <Text style={styles.grayColor} className="text-base">
-                                按每度電結算: {chargingWatt.split('~')[0]}
+                                {t('payment_finish.settled_per_kwh')}{chargingWatt.split('~')[0]}
                             </Text>
                         )}
 
                         <View className="h-0.5 my-3 bg-[#f4f4f4]" />
                         <View className="flex-row justify-between ">
-                            <Text className="text-xl">總計</Text>
+                            <Text className="text-xl">{t('payment_finish.total')}</Text>
                             <Text className="text-xl">HK$ {paymentFee}</Text>
                         </View>
                         <View className="mt-4"></View>
@@ -295,34 +69,23 @@ const PaymentFinishPageComponent = () => {
 
                     <View className="w-full h-1 bg-[#DBE4E8]" />
                     <View className="space-y-4 my-4">
-                        <Text className="text-xl ">付款資訊</Text>
+                        <Text className="text-xl ">{t('payment_finish.payment_info')}</Text>
                         <View>
                             <Text className="text-base" style={styles.grayColor}>
-                                訂單編號
+                                {t('payment_finish.order_number')}
                             </Text>
                             <Text className="text-base">{formatOrderId}</Text>
                         </View>
                         <View>
                             <Text className="text-base" style={styles.grayColor}>
-                                付款方式
+                                {t('payment_finish.payment_method')}
                             </Text>
-                            <Text className="text-base">預付銀包</Text>
+                            <Text className="text-base">{t('payment_finish.prepaid_wallet')}</Text>
                         </View>
-                        {/* <View>
-                            <Text
-                                className="text-base"
-                                style={styles.grayColor}
-                            >
-                                電郵地址
-                            </Text>
-                            <Text className="text-base">
-                                mikechan123@.com
-                            </Text>
-                        </View> */}
                     </View>
 
                     <NormalButton
-                        title={<Text style={{ color: '#fff', fontSize: 18 }}>下一頁</Text>}
+                        title={<Text style={{ color: '#fff', fontSize: 18 }}>{t('common.nextPage')}</Text>}
                         onPress={() => {
                             // router.replace('bookingSuccessPage');
                             router.replace({
@@ -346,4 +109,4 @@ const styles = StyleSheet.create({
     greenColor: {
         color: '#02677D'
     }
-});
+});

+ 14 - 15
component/chargingPage/paymentSummaryPageComponent.tsx

@@ -1,3 +1,4 @@
+// component/chargingPage/paymentSummaryPageComponent.tsx
 import { View, Text, ScrollView, StyleSheet, Pressable } from 'react-native';
 import { SafeAreaView } from 'react-native-safe-area-context';
 import { Alert } from 'react-native';
@@ -11,8 +12,10 @@ import useCouponStore from '../../providers/coupon_store';
 import { walletService } from '../../service/walletService';
 import { parseISO, subHours, format } from 'date-fns';
 import { chargeStationService } from '../../service/chargeStationService';
+import { useTranslation } from '../../util/hooks/useTranslation';
 
 const PaymentSummaryPageComponent = () => {
+    const { t } = useTranslation();
     const selectedCouponName = useCouponStore((state) => state.selectedCouponName);
     const selectedCouponRedeemCode = useCouponStore((state) => state.selectedCouponRedeemCode);
     const selectedCouponPrice = useCouponStore((state) => state.selectedCouponPrice);
@@ -129,16 +132,13 @@ const PaymentSummaryPageComponent = () => {
                 is_ic_call
             );
             if (response.status === 200 || response.status === 201) {
-                console.log('submit payment successful');
-
-                // router.push('/paymentFinishPage');
                 router.push({
                     pathname: '/paymentFinishPage',
                     params: { formatOrderId: response.data.format_order_id }
                 });
             } else if (response.status === 400) {
                 console.log('400 error in paymentSummaryPageComponent');
-                Alert.alert('餘額不足', '您的餘額不足,請充值後再試。');
+                Alert.alert(t('payment_summary.insufficient_balance_title'), t('payment_summary.insufficient_balance_message'));
             } else {
                 console.log('submit payment failed:', response);
             }
@@ -151,10 +151,10 @@ const PaymentSummaryPageComponent = () => {
         <SafeAreaView className="flex-1 bg-white" edges={['top', 'left', 'right']}>
             <ScrollView className="flex-1 mx-[5%]" showsVerticalScrollIndicator={false}>
                 <View style={{ marginTop: 25 }}>
-                    <Text style={{ fontSize: 45, paddingBottom: 12 }}>付款概要</Text>
+                    <Text style={{ fontSize: 45, paddingBottom: 12 }}>{t('payment_summary.title')}</Text>
                     <View className="flex-column">
                         <Pressable onPress={() => router.push('selectCouponPage')}>
-                            <Text className="text-lg pb-4">優惠券</Text>
+                            <Text className="text-lg pb-4">{t('payment_summary.coupon')}</Text>
                             {selectedCouponName === '' ? (
                                 <View
                                     style={{
@@ -166,7 +166,7 @@ const PaymentSummaryPageComponent = () => {
                                     className="rounded-xl h-[9vh] items-center flex-row pl-6 justify-between"
                                 >
                                     <View className="flex-row items-center ">
-                                        <Text className="color-[#999999] px-4 text-base">選擇優惠券</Text>
+                                        <Text className="color-[#999999] px-4 text-base">{t('payment_summary.select_coupon')}</Text>
                                     </View>
                                     <View className="pr-4">
                                         <GrayRightArrowIconSvg />
@@ -187,26 +187,25 @@ const PaymentSummaryPageComponent = () => {
                     </View>
 
                     <View>
-                        <Text className="text-xl py-4">收費概要</Text>
+                        <Text className="text-xl py-4">{t('payment_summary.fee_summary')}</Text>
                         <View className="flex-row justify-between">
-                            <Text className="text-base">充電費用</Text>
+                            <Text className="text-base">{t('payment_summary.charging_fee')}</Text>
                             <Text className="text-base">HK ${finalFee}</Text>
                         </View>
 
                         {chargingWatt === '' ? (
                             <Text style={styles.grayColor} className="text-base">
-                                充滿停機預估費用
+                                {t('payment_summary.estimated_full_charge')}
                             </Text>
                         ) : (
                             <Text style={styles.grayColor} className="text-base">
-                                按每度電結算:
-                                {chargingWatt?.split('~')[0]}
+                                {t('payment_summary.settled_per_kwh')}{chargingWatt?.split('~')[0]}
                             </Text>
                         )}
 
                         <View className="h-0.5 my-3 bg-[#f4f4f4]" />
                         <View className="flex-row justify-between ">
-                            <Text className="text-xl">總計</Text>
+                            <Text className="text-xl">{t('payment_summary.total')}</Text>
                             <Text className="text-3xl">
                                 HK$
                                 {finalFee}
@@ -222,7 +221,7 @@ const PaymentSummaryPageComponent = () => {
                                             fontWeight: '800'
                                         }}
                                     >
-                                        前往付款
+                                        {t('payment_summary.proceed_to_payment')}
                                     </Text>
                                 }
                                 onPress={
@@ -249,4 +248,4 @@ const styles = StyleSheet.create({
     greenColor: {
         color: '#02677D'
     }
-});
+});

+ 42 - 35
component/global/couponTabView.tsx

@@ -1,5 +1,4 @@
-//the size of the TabView will follow its parent-container's size.
-
+// component/global/couponTabView.tsx
 import * as React from 'react';
 import {
     View,
@@ -22,6 +21,7 @@ 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 +45,8 @@ const FirstRoute = ({
     loading: boolean;
     handleCouponClick: any;
 }) => {
+    const { t } = useTranslation();
+    
     return (
         <View className="flex-1">
             <ScrollView
@@ -65,7 +67,7 @@ const FirstRoute = ({
                                         coupon.is_consumed === false &&
                                         (coupon.expire_date === null || new Date(coupon.expire_date) > new Date())
                                 ).length === 0 ? (
-                                    <Text className="pl-4">暫時戶口沒有優惠券。</Text>
+                                    <Text className="pl-4">{t('selectCoupons.coupon.noCoupon')}</Text>
                                 ) : (
                                     coupons
                                         .filter(
@@ -103,32 +105,37 @@ const FirstRoute = ({
     );
 };
 
-const SecondRoute = ({ coupons }: { coupons: any }) => (
-    <ScrollView style={{ flex: 1, backgroundColor: 'white', marginTop: 14 }}>
-        <View className="flex-1 flex-col">
-            {coupons
-                .filter(
-                    (coupon: any) =>
-                        coupon.is_consumed === true ||
-                        (coupon.expire_date !== null && new Date(coupon.expire_date) < new Date())
-                )
-                .slice(0, 30)
-                .map((coupon: any, index: any) => (
-                    <IndividualCouponComponent
-                        key={`${coupon.id}-${index}`}
-                        title={coupon.coupon.name}
-                        price={coupon.coupon.amount}
-                        detail={coupon.coupon.description}
-                        date={formatCouponDate(coupon.expire_date)}
-                        setOpacity={true}
-                        noCircle={true}
-                    />
-                ))}
-        </View>
-    </ScrollView>
-);
+const SecondRoute = ({ coupons }: { coupons: any }) => {
+    const { t } = useTranslation();
+    
+    return (
+        <ScrollView style={{ flex: 1, backgroundColor: 'white', marginTop: 14 }}>
+            <View className="flex-1 flex-col">
+                {coupons
+                    .filter(
+                        (coupon: any) =>
+                            coupon.is_consumed === true ||
+                            (coupon.expire_date !== null && new Date(coupon.expire_date) < new Date())
+                    )
+                    .slice(0, 30)
+                    .map((coupon: any, index: any) => (
+                        <IndividualCouponComponent
+                            key={`${coupon.id}-${index}`}
+                            title={coupon.coupon.name}
+                            price={coupon.coupon.amount}
+                            detail={coupon.coupon.description}
+                            date={formatCouponDate(coupon.expire_date)}
+                            setOpacity={true}
+                            noCircle={true}
+                        />
+                    ))}
+            </View>
+        </ScrollView>
+    );
+};
 
 const CouponTabViewComponent: React.FC<TabViewComponentProps> = ({ titles }) => {
+    const { t } = useTranslation();
     const layout = useWindowDimensions();
     const [loading, setLoading] = useState(false);
     const [coupons, setCoupons] = useState([]);
@@ -173,9 +180,9 @@ const CouponTabViewComponent: React.FC<TabViewComponentProps> = ({ titles }) =>
                 // More specific error handling
                 if (axios.isAxiosError(error)) {
                     const errorMessage = error.response?.data?.message || 'Network error occurred';
-                    Alert.alert('Error', `Unable to fetch price: ${errorMessage}`, [
+                    Alert.alert(t('common.error'), `${t('selectCoupons.error_fetching_balance')}: ${errorMessage}`, [
                         {
-                            text: 'OK',
+                            text: t('common.ok'),
                             onPress: () => {
                                 cleanupData();
                                 router.push('/mainPage');
@@ -183,9 +190,9 @@ const CouponTabViewComponent: React.FC<TabViewComponentProps> = ({ titles }) =>
                         }
                     ]);
                 } else {
-                    Alert.alert('Error', 'An unexpected error occurred while fetching the price', [
+                    Alert.alert(t('common.error'), t('selectCoupons.error_fetching_balance'), [
                         {
-                            text: 'OK',
+                            text: t('common.ok'),
                             onPress: () => {
                                 cleanupData();
                                 router.push('/mainPage');
@@ -201,9 +208,9 @@ const CouponTabViewComponent: React.FC<TabViewComponentProps> = ({ titles }) =>
     const handleCouponClick = async (clickedCoupon: string) => {
         let temp_promotion_code = [...promotion_code];
         if (!current_price_store) {
-            Alert.alert('Error', 'Unable to fetch price', [
+            Alert.alert(t('common.error'), t('selectCoupons.error_fetching_balance'), [
                 {
-                    text: 'OK',
+                    text: t('common.ok'),
                     onPress: () => {
                         cleanupData();
                         router.push('/mainPage');
@@ -225,7 +232,7 @@ const CouponTabViewComponent: React.FC<TabViewComponentProps> = ({ titles }) =>
                     setCouponDetail([...coupon_detail, found_coupon]);
                     setPromotionCode([...promotion_code, clickedCoupon]);
                 } else {
-                    Alert.alert('不符合使用優惠券的條件', '請查看優惠卷的詳情,例如是否需要滿足最低消費金額。');
+                    Alert.alert(t('selectCoupons.coupon.invalid_condition_title'), t('selectCoupons.coupon.invalid_condition_message'));
                 }
             } catch (error) {
                 console.log(error);
@@ -318,4 +325,4 @@ const styles = StyleSheet.create({
         shadowOpacity: 0.25,
         shadowRadius: 3.84
     }
-});
+});

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

@@ -137,7 +137,16 @@
     "confirm": "Confirm",
     "cancel": "Cancel",
     "title": "",
-    "agree": "Agree and continue"
+    "agree": "Agree and Continue",
+    "use_now": "Use Now",
+    "permanent": "Permanent",
+    "to_date": "to {{date}}",
+    "error": "Error",
+    "ok": "OK",
+    "loading": "Loading...",
+    "processing": "Processing...",
+    "please_wait": "Please wait...",
+    "nextPage": "Next Page"
   },
   "home": {
     "vehicle_info_error_title": "Unable to detect vehicle information",
@@ -228,7 +237,8 @@
     },
     "coupon": {
       "valid_until": "Valid until",
-      "noCoupon": "There are currently no coupons available for household registration."
+      "noCoupon": "There are currently no coupons available for household registration.",
+      "cash_voucher": "Cash Voucher"
     },
     "error_fetching_coupons": "Error fetching coupons:",
     "payment_timeout_title": "Payment Timeout",
@@ -254,6 +264,29 @@
       "used_expired": "Used/Expired"
     }
   },
+  "selectCoupons": {
+    "coupons": {
+      "title": "Coupons",
+      "available": "Available Coupons",
+      "used_expired": "Used/Expired",
+      "valid_until": "Valid until",
+      "cash_voucher": "Cash Voucher"
+    },
+    "coupon_details": "Coupon Details",
+    "coupon": {
+      "terms_and_conditions": "Terms and Conditions",
+      "term1": "・ The holder of this coupon can exchange it for an equivalent value of charging service at any Crazy Charge store in Hong Kong within its validity period. Expired coupons are invalid.",
+      "term2": "・ When using this coupon, the electricity fee will be calculated at the regular price of $3.5 per kWh, and it is not applicable to owl-hour periods or other discounted electricity pricing periods.",
+      "term3": "・ This coupon cannot be exchanged for cash or other denominations of cash vouchers. The holder is not entitled to cash or other forms of change.",
+      "term4": "・ Once a user confirms the use of an electronic coupon within this app, it is deemed that they agree to consume the discount according to the coupon rules. The relevant coupon will be immediately deducted from the account and cannot be refunded after deduction.",
+      "term5": "・ Even if the actual charging consumption amount does not reach the face value of the electronic coupon, no refund will be made for the difference. The use of coupons is intended to provide users with charging discounts, not as a cash exchange or refund tool.",
+      "term6": "・ In case of any dispute, Crazy Charge reserves the right to change the terms and conditions regarding the use of this cash voucher without prior notice.",
+      "noCoupon": "No coupons available at the moment.",
+      "invalid_condition_title": "Coupon usage conditions not met",
+      "invalid_condition_message": "Please check the coupon details, such as minimum spending requirements."
+    },
+    "error_fetching_balance": "Error fetching wallet balance"
+  },
   "payment_record": {
     "title": "Payment Records",
     "balance_label": "Balance (HKD)",
@@ -319,6 +352,79 @@
       "others": "Others"
     }
   },
+  "chargingOption": {
+    "option": {
+      "select_charge_amount": "Select Charging Amount",
+      "kwh_20": "20kWh",
+      "kwh_25": "25kWh",
+      "kwh_30": "30kWh",
+      "kwh_40": "40kWh",
+      "full_charge": "Full Charge",
+      "min_25": "25 mins",
+      "min_30": "30 mins",
+      "min_40": "40 mins",
+      "min_45": "45 mins",
+      "up_to_80kwh": "Up to 80kWh",
+      "coupon_value": "$ {{value}} off",
+      "available_coupon": "Coupons Available",
+      "no_coupon": "No Coupons",
+      "alert": {
+        "select_plan_title": "Select Charging Plan",
+        "select_plan_message": "Please select a charging plan before choosing coupons",
+        "confirm_plan_message": "Please select a charging plan before confirming",
+        "reminder_title": "Reminder",
+        "reminder_message": "Transactions using coupons will be calculated at the regular price of $3.5 per kWh!"
+      }
+    }
+
+  },
+  "payment": {
+    "summary_title": "Payment Summary",
+    "charging_fee": "Charging Fee",
+    "settled_kwh": "Settled kWh",
+    "full_charge": "Full Charge",
+    "price_per_kwh": "Price per kWh",
+    "note": "Note: Cross-period charging will be charged separately based on unit prices of different periods, and the actual charging amount may fluctuate",
+    "coupon": "Coupons",
+    "total": "Total",
+    "confirm_payment": "Confirm Payment",
+    "wait_loading_price": "Still loading price information...",
+    "failed_fetch_udcc": "Failed to fetch UDCC, please try again later",
+    "failed_fetch_udc": "Failed to fetch UDC, please try again later",
+    "failed_fetch_userid": "Failed to fetch userID, please try again later",
+    "failed_fetch_wallet": "Failed to fetch user wallet, please try again later",
+    "failed_fetch_reservations": "Failed to fetch reservation histories for penalty checking purpose",
+    "failed_fetch_totalprice": "Unable to fetch totalPrice",
+    "unknown_error": "Unknown error",
+    "back_main": "Back to Main Page",
+    "failed_fetch_outtrade": "Failed to fetch outTradeNo",
+    "insufficient_balance_redirect": "Detected insufficient wallet balance, redirecting to top-up page now"
+  },
+  "payment_summary": {
+    "title": "Payment Summary",
+    "coupon": "Coupons",
+    "select_coupon": "Select Coupon",
+    "fee_summary": "Fee Summary",
+    "charging_fee": "Charging Fee",
+    "estimated_full_charge": "Estimated Full Charge Fee",
+    "settled_per_kwh": "Settled per kWh: ",
+    "total": "Total",
+    "proceed_to_payment": "Proceed to Payment",
+    "insufficient_balance_title": "Insufficient Balance",
+    "insufficient_balance_message": "Your balance is insufficient. Please top up and try again."
+  },
+  "payment_finish": {
+    "success_title": "Payment Successful",
+    "fee_summary": "Fee Summary",
+    "charging_fee": "Charging Fee",
+    "estimated_full_charge": "Estimated Full Charge Fee",
+    "settled_per_kwh": "Settled per kWh: ",
+    "total": "Total",
+    "payment_info": "Payment Information",
+    "order_number": "Order Number",
+    "payment_method": "Payment Method",
+    "prepaid_wallet": "Prepaid Wallet"
+  },
   "account": {
     "wallet": "Wallet",
     "my_vehicle": "My Vehicle",
@@ -440,6 +546,10 @@
       "changing": "Changing..."
     }
   },
+  "assistance": {
+    "troubleshooting": "Troubleshooting",
+    "description": "If you encounter any problems while using the app, please contact our customer service via WhatsApp:"
+  },
   "auth": {
     "email": "Email",
     "password": "Password",

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

@@ -135,7 +135,16 @@
     "confirm": "確認",
     "cancel": "取消",
     "title": "",
-    "agree": "同意並繼續"
+    "agree": "同意並繼續",
+    "use_now": "馬上使用",
+    "permanent": "永久",
+    "to_date": "至 {{date}}",
+    "error": "錯誤",
+    "ok": "確定",
+    "loading": "載入中...",
+    "processing": "處理中...",
+    "please_wait": "請稍候...",
+    "nextPage": "下一頁"
   },
   "home": {
     "vehicle_info_error_title": "無法檢測車輛資訊",
@@ -226,7 +235,8 @@
     },
     "coupon": {
       "valid_until": "有效期至",
-      "noCoupon": "暫時戶口沒有優惠券。"
+      "noCoupon": "暫時戶口沒有優惠券。",
+      "cash_voucher": "現金劵"
     },
     "error_fetching_coupons": "Error fetching coupons:",
     "payment_timeout_title": "Payment Timeout",
@@ -252,6 +262,29 @@
       "used_expired": "已使用/失效"
     }
   },
+  "selectCoupons": {
+    "coupons": {
+      "title": "優惠券",
+      "available": "可用優惠券",
+      "used_expired": "已使用/失效",
+      "valid_until": "有效期",
+      "cash_voucher": "現金劵"
+    },
+    "coupon_details": "優惠券細節",
+    "coupon": {
+      "terms_and_conditions": "服務條款與細則",
+      "term1": "・ 此券持有人可在本券有效期內於任何位於Crazy Charge 之香港分店換取同等價值充電服務,逾期無效。",
+      "term2": "・ 此優惠券使用時,電費將以正常價格$3.5元/每度電計算,不適用於貓頭鷹時段或其他折扣時段的電力價格計算。",
+      "term3": "・ 此券不能用以套换現金或其他面值之現金券,持有人不獲現金或其他形式之找贖。",
+      "term4": "・ 使用者一旦在本 APP 內確認使用電子優惠券,即視為同意依優惠券規則進行消費抵扣,相關優惠券將立即從帳戶中扣除,且扣除後不得退還。",
+      "term5": "・ 即便實際充電消費金額未達到電子優惠券的面額,亦不會就差額部分進行退款。優惠券的使用旨在為用戶提供充電優惠,而非現金兌換或退款工具。",
+      "term6": "・ 如有任何爭議,Crazy Charge 保留更改有關使用此現金券之條款及細則,而毋須另行通知。",
+      "noCoupon": "暫時戶口沒有優惠券。",
+      "invalid_condition_title": "不符合使用優惠券的條件",
+      "invalid_condition_message": "請查看優惠卷的詳情,例如是否需要滿足最低消費金額。"
+    },
+     "error_fetching_balance": "獲取錢包餘額時出錯"
+  },
   "payment_record": {
     "title": "訂單記錄",
     "balance_label": "餘額 (HKD)",
@@ -317,6 +350,78 @@
       "others": "其他"
     }
   },
+  "chargingOption": {
+    "option": {
+      "select_charge_amount": "選擇充電度數",
+      "kwh_20": "20度電",
+      "kwh_25": "25度電",
+      "kwh_30": "30度電",
+      "kwh_40": "40度電",
+      "full_charge": "充滿停機",
+      "min_25": "25分鐘",
+      "min_30": "30分鐘",
+      "min_40": "40分鐘",
+      "min_45": "45分鐘",
+      "up_to_80kwh": "最多80度電",
+      "coupon_value": "$ {{value}}劵",
+      "available_coupon": "有可用劵",
+      "no_coupon": "無可用劵",
+      "alert": {
+        "select_plan_title": "選擇充電方案",
+        "select_plan_message": "請先選擇充電方案再選擇優惠券",
+        "confirm_plan_message": "請先選擇充電方案再確認",
+        "reminder_title": "提醒您",
+        "reminder_message": "使用優惠券的交易每度電以正價$3.5元計算!"
+      }
+    }
+  },
+  "payment": {
+    "summary_title": "付款概要",
+    "charging_fee": "充電費用",
+    "settled_kwh": "結算電度數",
+    "full_charge": "充滿停機",
+    "price_per_kwh": "每度電價錢",
+    "note": "注: 跨時段充電將按不同時段的單價分別計费,實際充電量可能浮動",
+    "coupon": "優惠劵",
+    "total": "總計",
+    "confirm_payment": "付款確認",
+    "wait_loading_price": "仍在加載價格信息...",
+    "failed_fetch_udcc": "無法獲取UDCC,請稍後再試",
+    "failed_fetch_udc": "無法獲取UDC,請稍後再試",
+    "failed_fetch_userid": "無法獲取用戶ID,請稍後再試",
+    "failed_fetch_wallet": "無法獲取用戶錢包,請稍後再試",
+    "failed_fetch_reservations": "無法獲取預訂歷史記錄以進行罰款檢查",
+    "failed_fetch_totalprice": "無法獲取總價格",
+    "unknown_error": "未知錯誤",
+    "back_main": "返回主頁",
+    "failed_fetch_outtrade": "無法獲取outTradeNo",
+    "insufficient_balance_redirect": "偵測到您錢包餘額不足,現在為您跳轉到充值頁面"
+  },
+  "payment_summary": {
+    "title": "付款概要",
+    "coupon": "優惠券",
+    "select_coupon": "選擇優惠券",
+    "fee_summary": "收費概要",
+    "charging_fee": "充電費用",
+    "estimated_full_charge": "充滿停機預估費用",
+    "settled_per_kwh": "按每度電結算:",
+    "total": "總計",
+    "proceed_to_payment": "前往付款",
+    "insufficient_balance_title": "餘額不足",
+    "insufficient_balance_message": "您的餘額不足,請充值後再試。"
+  },
+  "payment_finish": {
+    "success_title": "成功付款",
+    "fee_summary": "收費概要",
+    "charging_fee": "充電費用",
+    "estimated_full_charge": "充滿停機預估費用",
+    "settled_per_kwh": "按每度電結算: ",
+    "total": "總計",
+    "payment_info": "付款資訊",
+    "order_number": "訂單編號",
+    "payment_method": "付款方式",
+    "prepaid_wallet": "預付銀包"
+  },
   "account": {
     "wallet": "錢包",
     "my_vehicle": "我的車輛",
@@ -438,6 +543,10 @@
       "changing": "更改中..."
     }
   },
+  "assistance": {
+    "troubleshooting": "排除解難",
+    "description": "如果您在使用該應用程式時遇到任何問題,請透過WhatsApp聯絡我們的客戶服務人員:"
+  },
   "auth": {
     "email": "邮箱",
     "password": "密码",