lang-option

This commit is contained in:
kirukib 2025-12-21 23:47:01 +03:00
parent 3d66182b95
commit fb6d47409f
7 changed files with 675 additions and 91 deletions

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom'; import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { LanguageProvider } from './contexts/LanguageContext';
import { ChatButton } from './components/ui/ChatButton'; import { ChatButton } from './components/ui/ChatButton';
import { AccountSelectionPage } from './pages/AccountSelectionPage'; import { AccountSelectionPage } from './pages/AccountSelectionPage';
import { CheckoutPageRoute } from './pages/CheckoutPageRoute'; import { CheckoutPageRoute } from './pages/CheckoutPageRoute';
@ -30,10 +31,12 @@ const AppRoutes: React.FC = () => {
function App() { function App() {
return ( return (
<LanguageProvider>
<BrowserRouter> <BrowserRouter>
<AppRoutes /> <AppRoutes />
<ChatButton /> <ChatButton />
</BrowserRouter> </BrowserRouter>
</LanguageProvider>
); );
} }

View File

@ -5,6 +5,7 @@ import { Input } from '../ui/Input';
import { Card } from '../ui/Card'; import { Card } from '../ui/Card';
import { Logo } from '../ui/Logo'; import { Logo } from '../ui/Logo';
import { DonationSection } from './DonationSection'; import { DonationSection } from './DonationSection';
import { useLanguage } from '../../contexts/LanguageContext';
import { FiLock, FiArrowLeft, FiEye, FiEyeOff, FiInfo, FiShield, FiCheck, FiCreditCard } from 'react-icons/fi'; import { FiLock, FiArrowLeft, FiEye, FiEyeOff, FiInfo, FiShield, FiCheck, FiCreditCard } from 'react-icons/fi';
interface CheckoutFormData { interface CheckoutFormData {
@ -44,6 +45,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
const [referralCode, setReferralCode] = useState(''); const [referralCode, setReferralCode] = useState('');
const [paymentMethod, setPaymentMethod] = useState<'card' | 'googlepay' | 'applepay'>('card'); const [paymentMethod, setPaymentMethod] = useState<'card' | 'googlepay' | 'applepay'>('card');
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useLanguage();
const [isProcessing, setIsProcessing] = useState(false); const [isProcessing, setIsProcessing] = useState(false);
@ -189,7 +191,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
.{((total % 1) * 100).toFixed(0).padStart(2, '0')} .{((total % 1) * 100).toFixed(0).padStart(2, '0')}
</span> </span>
</div> </div>
<p className="text-primary-100 text-sm">Total amount</p> <p className="text-primary-100 text-sm">{t('checkout.totalAmount')}</p>
</div> </div>
{/* Selected Plan/Transaction Box */} {/* Selected Plan/Transaction Box */}
@ -213,20 +215,20 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Order Summary */} {/* Order Summary */}
<div className="mb-8"> <div className="mb-8">
<h3 className="text-lg font-semibold text-white mb-4">Order Summary</h3> <h3 className="text-lg font-semibold text-white mb-4">{t('checkout.orderSummary')}</h3>
<div className="space-y-3 mb-4"> <div className="space-y-3 mb-4">
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span className="text-primary-100">Subtotal</span> <span className="text-primary-100">{t('common.subtotal')}</span>
<span className="text-white">{formatCurrency(subtotal)}</span> <span className="text-white">{formatCurrency(subtotal)}</span>
</div> </div>
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span className="text-primary-100">Transaction Fee</span> <span className="text-primary-100">{t('checkout.transactionFee')}</span>
<span className="text-white">{formatCurrency(transactionFee)}</span> <span className="text-white">{formatCurrency(transactionFee)}</span>
</div> </div>
{donationAmount > 0 && ( {donationAmount > 0 && (
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span className="text-primary-100">Donation</span> <span className="text-primary-100">{t('common.donation')}</span>
<span className="text-white">{formatCurrency(donationAmount)}</span> <span className="text-white">{formatCurrency(donationAmount)}</span>
</div> </div>
)} )}
@ -235,11 +237,11 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Referral Code Input */} {/* Referral Code Input */}
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm text-primary-100 mb-2"> <label className="block text-sm text-primary-100 mb-2">
Referral Code {t('checkout.referralCode')}
</label> </label>
<input <input
type="text" type="text"
placeholder="Enter referral code" placeholder={t('checkout.enterCode')}
value={referralCode} value={referralCode}
onChange={(e) => setReferralCode(e.target.value)} onChange={(e) => setReferralCode(e.target.value)}
className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-primary-200 focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-white/40" className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-primary-200 focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-white/40"
@ -248,7 +250,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Total Due */} {/* Total Due */}
<div className="flex justify-between items-center pt-2"> <div className="flex justify-between items-center pt-2">
<span className="text-lg font-bold text-white">Total due today</span> <span className="text-lg font-bold text-white">{t('checkout.totalDue')}</span>
<span className="text-2xl font-bold text-white"> <span className="text-2xl font-bold text-white">
{formatCurrency(total)} {formatCurrency(total)}
</span> </span>
@ -262,10 +264,10 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
</p> </p>
<div className="flex gap-4 text-xs"> <div className="flex gap-4 text-xs">
<a href="#" className="text-primary-100 hover:text-white underline"> <a href="#" className="text-primary-100 hover:text-white underline">
Terms {t('common.terms')}
</a> </a>
<a href="#" className="text-primary-100 hover:text-white underline"> <a href="#" className="text-primary-100 hover:text-white underline">
Privacy {t('common.privacy')}
</a> </a>
</div> </div>
</div> </div>
@ -276,7 +278,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<div className="bg-gray-50 overflow-y-auto"> <div className="bg-gray-50 overflow-y-auto">
<div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12"> <div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> <form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<h2 className="text-2xl font-bold text-gray-900 mb-6">Payment Method</h2> <h2 className="text-2xl font-bold text-gray-900 mb-6">{t('checkout.paymentMethod')}</h2>
{/* Payment Method Selector */} {/* Payment Method Selector */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-6"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-6">
@ -293,7 +295,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<div className="flex flex-col items-center gap-2"> <div className="flex flex-col items-center gap-2">
<FiCreditCard className={`w-6 h-6 ${paymentMethod === 'card' ? 'text-primary-500' : 'text-gray-600'}`} /> <FiCreditCard className={`w-6 h-6 ${paymentMethod === 'card' ? 'text-primary-500' : 'text-gray-600'}`} />
<span className={`text-sm font-medium ${paymentMethod === 'card' ? 'text-primary-600' : 'text-gray-700'}`}> <span className={`text-sm font-medium ${paymentMethod === 'card' ? 'text-primary-600' : 'text-gray-700'}`}>
Card {t('payment.card')}
</span> </span>
</div> </div>
</button> </button>
@ -316,7 +318,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/> <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
</svg> </svg>
<span className={`text-sm font-medium ${paymentMethod === 'googlepay' ? 'text-primary-600' : 'text-gray-700'}`}> <span className={`text-sm font-medium ${paymentMethod === 'googlepay' ? 'text-primary-600' : 'text-gray-700'}`}>
Google Pay {t('payment.googlePay')}
</span> </span>
</div> </div>
</button> </button>
@ -336,7 +338,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/> <path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
</svg> </svg>
<span className={`text-sm font-medium ${paymentMethod === 'applepay' ? 'text-primary-600' : 'text-gray-700'}`}> <span className={`text-sm font-medium ${paymentMethod === 'applepay' ? 'text-primary-600' : 'text-gray-700'}`}>
Apple Pay {t('payment.applePay')}
</span> </span>
</div> </div>
</button> </button>
@ -348,15 +350,15 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Email */} {/* Email */}
<div> <div>
<Input <Input
label="Email" label={t('common.email')}
type="email" type="email"
placeholder="Email" placeholder={t('common.email')}
error={errors.email?.message} error={errors.email?.message}
{...register('email', { {...register('email', {
required: 'Email is required', required: t('error.required'),
pattern: { pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address', message: t('error.invalidEmail'),
}, },
})} })}
/> />
@ -365,7 +367,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Card Details */} {/* Card Details */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Card details {t('checkout.cardDetails')}
</label> </label>
<div className="space-y-4"> <div className="space-y-4">
{/* Card Number */} {/* Card Number */}
@ -378,7 +380,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
errors.cardNumber ? 'border-red-500' : 'border-gray-300' errors.cardNumber ? 'border-red-500' : 'border-gray-300'
}`} }`}
{...register('cardNumber', { {...register('cardNumber', {
required: 'Card number is required', required: t('error.required'),
onChange: (e) => { onChange: (e) => {
const formatted = formatCardNumber(e.target.value); const formatted = formatCardNumber(e.target.value);
e.target.value = formatted; e.target.value = formatted;
@ -427,7 +429,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
errors.expiryDate ? 'border-red-500' : 'border-gray-300' errors.expiryDate ? 'border-red-500' : 'border-gray-300'
}`} }`}
{...register('expiryDate', { {...register('expiryDate', {
required: 'Expiry date is required', required: t('error.required'),
})} })}
/> />
{errors.expiryDate && ( {errors.expiryDate && (
@ -443,7 +445,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
errors.cvv ? 'border-red-500' : 'border-gray-300' errors.cvv ? 'border-red-500' : 'border-gray-300'
}`} }`}
{...register('cvv', { {...register('cvv', {
required: 'CVC is required', required: t('error.required'),
})} })}
/> />
<FiLock className="absolute right-3 top-1/2 transform -translate-y-1/2 text-secondary-500" /> <FiLock className="absolute right-3 top-1/2 transform -translate-y-1/2 text-secondary-500" />
@ -458,12 +460,12 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Name on card */} {/* Name on card */}
<div> <div>
<Input <Input
label="Name on card" label={t('checkout.nameOnCard')}
type="text" type="text"
placeholder="Name on card" placeholder={t('checkout.nameOnCard')}
error={errors.cardName?.message} error={errors.cardName?.message}
{...register('cardName', { {...register('cardName', {
required: 'Name on card is required', required: t('error.required'),
})} })}
/> />
</div> </div>
@ -474,12 +476,12 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
{/* Billing Address */} {/* Billing Address */}
<div> <div>
<Input <Input
label="Billing address" label={t('checkout.billingAddress')}
type="text" type="text"
placeholder="Address" placeholder="Address"
error={errors.address?.message} error={errors.address?.message}
{...register('address', { {...register('address', {
required: 'Address is required', required: t('error.required'),
})} })}
/> />
</div> </div>
@ -500,7 +502,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500" className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
/> />
<label htmlFor="agreeToTerms" className="text-sm text-gray-700"> <label htmlFor="agreeToTerms" className="text-sm text-gray-700">
By confirming your payment, you allow us to charge your card for this and future payments in accordance with terms. You can always cancel your subscription. {t('checkout.agreeTerms')}
</label> </label>
</div> </div>
{errors.agreeToTerms && ( {errors.agreeToTerms && (
@ -513,7 +515,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
disabled={isSubmitting} disabled={isSubmitting}
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-4 px-6 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-4 px-6 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
> >
{isSubmitting ? 'Processing...' : 'Pay'} {isSubmitting ? t('common.processing') : t('common.pay')}
</button> </button>
</> </>
)} )}
@ -548,7 +550,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/> <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/> <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
</svg> </svg>
<span>{isProcessing ? 'Processing...' : 'Pay with Google Pay'}</span> <span>{isProcessing ? t('common.processing') : t('payment.payWithGoogle')}</span>
</button> </button>
</> </>
)} )}
@ -580,7 +582,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<svg className="w-6 h-6" viewBox="0 0 24 24" fill="currentColor"> <svg className="w-6 h-6" viewBox="0 0 24 24" fill="currentColor">
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/> <path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
</svg> </svg>
<span>{isProcessing ? 'Processing...' : 'Pay with Apple Pay'}</span> <span>{isProcessing ? t('common.processing') : t('payment.payWithApple')}</span>
</button> </button>
</> </>
)} )}
@ -599,12 +601,12 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
type="checkbox" type="checkbox"
id="agreeToTermsAlt" id="agreeToTermsAlt"
{...register('agreeToTerms', { {...register('agreeToTerms', {
required: 'You must agree to the terms and conditions', required: t('error.required'),
})} })}
className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500" className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
/> />
<label htmlFor="agreeToTermsAlt" className="text-sm text-gray-700"> <label htmlFor="agreeToTermsAlt" className="text-sm text-gray-700">
By confirming your payment, you allow us to charge your card for this and future payments in accordance with terms. You can always cancel your subscription. {t('checkout.agreeTerms')}
</label> </label>
</div> </div>
{errors.agreeToTerms && ( {errors.agreeToTerms && (
@ -617,7 +619,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
<div className="flex items-center justify-center gap-2 pt-4 border-t border-gray-200"> <div className="flex items-center justify-center gap-2 pt-4 border-t border-gray-200">
<div className="flex items-center gap-2 text-sm text-gray-600"> <div className="flex items-center gap-2 text-sm text-gray-600">
<FiShield className="w-5 h-5 text-secondary-500" /> <FiShield className="w-5 h-5 text-secondary-500" />
<span className="font-medium">Verified checkout page by</span> <span className="font-medium">{t('checkout.verifiedBy')}</span>
<span className="font-bold text-primary-500">AmbaPay</span> <span className="font-bold text-primary-500">AmbaPay</span>
<FiCheck className="w-4 h-4 text-secondary-500" /> <FiCheck className="w-4 h-4 text-secondary-500" />
</div> </div>

View File

@ -1,4 +1,5 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import { useLanguage } from "../../contexts/LanguageContext";
import { FiHeart, FiX, FiChevronLeft, FiChevronRight } from "react-icons/fi"; import { FiHeart, FiX, FiChevronLeft, FiChevronRight } from "react-icons/fi";
interface DonationCause { interface DonationCause {
@ -43,6 +44,7 @@ const mockCauses: DonationCause[] = [
export const DonationSection: React.FC<DonationSectionProps> = ({ export const DonationSection: React.FC<DonationSectionProps> = ({
onDonationChange, onDonationChange,
}) => { }) => {
const { t } = useLanguage();
const [donationAmount, setDonationAmount] = useState(""); const [donationAmount, setDonationAmount] = useState("");
const [selectedCause, setSelectedCause] = useState<DonationCause | null>( const [selectedCause, setSelectedCause] = useState<DonationCause | null>(
null null
@ -111,7 +113,12 @@ export const DonationSection: React.FC<DonationSectionProps> = ({
// Add "None" option to causes // Add "None" option to causes
const allCauses = [ const allCauses = [
{ id: "none", name: "None", description: "No donation", category: "None" }, {
id: "none",
name: t("donation.none"),
description: t("donation.noDonation"),
category: "None",
},
...mockCauses, ...mockCauses,
]; ];
@ -119,7 +126,7 @@ export const DonationSection: React.FC<DonationSectionProps> = ({
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Add donation to institution {t("donation.addDonation")}
</label> </label>
<input <input
type="number" type="number"
@ -134,7 +141,7 @@ export const DonationSection: React.FC<DonationSectionProps> = ({
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Select Cause {t("donation.selectCause")}
</label> </label>
<div className="relative"> <div className="relative">
{/* Left Scroll Button */} {/* Left Scroll Button */}

View File

@ -1,8 +1,11 @@
import React from 'react'; import React from 'react';
import { Logo } from '../ui/Logo'; import { Logo } from '../ui/Logo';
import { useLanguage } from '../../contexts/LanguageContext';
import { FiFacebook, FiTwitter, FiInstagram, FiLinkedin, FiSmartphone } from 'react-icons/fi'; import { FiFacebook, FiTwitter, FiInstagram, FiLinkedin, FiSmartphone } from 'react-icons/fi';
export const PromotionalPanel: React.FC = () => { export const PromotionalPanel: React.FC = () => {
const { t } = useLanguage();
return ( return (
<div className="bg-primary-500 h-full flex flex-col justify-between p-8 md:p-12 text-white relative"> <div className="bg-primary-500 h-full flex flex-col justify-between p-8 md:p-12 text-white relative">
<div <div
@ -17,30 +20,30 @@ export const PromotionalPanel: React.FC = () => {
</div> </div>
<div className="flex-1 flex flex-col justify-center items-center text-center"> <div className="flex-1 flex flex-col justify-center items-center text-center">
<h1 className="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold mb-6 max-w-2xl"> <h1 className="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold mb-6 max-w-2xl">
Start searching for homes or posting ads now. {t('promo.title')}
</h1> </h1>
<p className="text-primary-100 text-base md:text-lg max-w-xl"> <p className="text-primary-100 text-base md:text-lg max-w-xl">
Interfaces are well designed for all ages and target audiences are extremely simple and work with social media integrations {t('promo.description')}
</p> </p>
</div> </div>
<div className="mt-auto"> <div className="mt-auto">
{/* Download App Buttons */} {/* Download App Buttons */}
<div className="mb-6"> <div className="mb-6">
<p className="text-sm text-primary-100 text-center mb-3">Download the Amba App</p> <p className="text-sm text-primary-100 text-center mb-3">{t('promo.downloadApp')}</p>
<div className="flex gap-3 justify-center"> <div className="flex gap-3 justify-center">
<button <button
onClick={() => window.open('https://apps.apple.com/app/amba', '_blank')} onClick={() => window.open('https://apps.apple.com/app/amba', '_blank')}
className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20" className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20"
> >
<FiSmartphone className="w-4 h-4" /> <FiSmartphone className="w-4 h-4" />
<span className="text-sm font-medium">iOS</span> <span className="text-sm font-medium">{t('promo.ios')}</span>
</button> </button>
<button <button
onClick={() => window.open('https://play.google.com/store/apps/details?id=com.amba', '_blank')} onClick={() => window.open('https://play.google.com/store/apps/details?id=com.amba', '_blank')}
className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20" className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20"
> >
<FiSmartphone className="w-4 h-4" /> <FiSmartphone className="w-4 h-4" />
<span className="text-sm font-medium">Android</span> <span className="text-sm font-medium">{t('promo.android')}</span>
</button> </button>
</div> </div>
</div> </div>
@ -80,11 +83,11 @@ export const PromotionalPanel: React.FC = () => {
{/* Footer Links */} {/* Footer Links */}
<div className="flex gap-4 justify-center text-sm text-primary-100 pb-4"> <div className="flex gap-4 justify-center text-sm text-primary-100 pb-4">
<a href="#" className="hover:text-white underline"> <a href="#" className="hover:text-white underline">
Terms {t('common.terms')}
</a> </a>
<span className="text-primary-200"></span> <span className="text-primary-200"></span>
<a href="#" className="hover:text-white underline"> <a href="#" className="hover:text-white underline">
Privacy Policy {t('common.privacy')}
</a> </a>
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import { Card } from "../ui/Card"; import { Card } from "../ui/Card";
import { PromotionalPanel } from "./PromotionalPanel"; import { PromotionalPanel } from "./PromotionalPanel";
import type { RecipientBankAccount } from "../../types"; import type { RecipientBankAccount } from "../../types";
import { useLanguage, languages } from "../../contexts/LanguageContext";
import { import {
FiBriefcase, FiBriefcase,
FiUser, FiUser,
@ -38,15 +39,9 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
const [showFlagModal, setShowFlagModal] = useState(false); const [showFlagModal, setShowFlagModal] = useState(false);
const [flagReason, setFlagReason] = useState(""); const [flagReason, setFlagReason] = useState("");
const [showLanguageMenu, setShowLanguageMenu] = useState(false); const [showLanguageMenu, setShowLanguageMenu] = useState(false);
const [selectedLanguage, setSelectedLanguage] = useState("English"); const { currentLanguage, setLanguage, t } = useLanguage();
const languages = [ const currentLangName = languages.find(l => l.code === currentLanguage)?.name || 'English';
{ code: "en", name: "English" },
{ code: "fr", name: "French" },
{ code: "am", name: "Amharic" },
{ code: "om", name: "Oromo" },
{ code: "ti", name: "Tigrinya" },
];
const formatCurrency = (amount: number) => { const formatCurrency = (amount: number) => {
return new Intl.NumberFormat("en-US", { return new Intl.NumberFormat("en-US", {
style: "currency", style: "currency",
@ -75,7 +70,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
<div className="mb-8"> <div className="mb-8">
<div className="flex items-center justify-between mb-4 flex-wrap gap-2"> <div className="flex items-center justify-between mb-4 flex-wrap gap-2">
<h2 className="text-lg sm:text-xl font-bold text-gray-800 break-words"> <h2 className="text-lg sm:text-xl font-bold text-gray-800 break-words">
Payment Request {t('requester.paymentRequest')}
</h2> </h2>
<div className="flex items-center gap-2 flex-shrink-0"> <div className="flex items-center gap-2 flex-shrink-0">
{/* Language Selector */} {/* Language Selector */}
@ -86,7 +81,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
> >
<FiGlobe className="w-4 h-4 text-secondary-500" /> <FiGlobe className="w-4 h-4 text-secondary-500" />
<span className="font-medium text-sm"> <span className="font-medium text-sm">
{selectedLanguage} {currentLangName}
</span> </span>
<FiChevronDown <FiChevronDown
className={`w-4 h-4 transition-transform ${ className={`w-4 h-4 transition-transform ${
@ -106,13 +101,11 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
<button <button
key={lang.code} key={lang.code}
onClick={() => { onClick={() => {
setSelectedLanguage(lang.name); setLanguage(lang.code);
setShowLanguageMenu(false); setShowLanguageMenu(false);
// Here you would typically update the app language
// For now, we'll just update the selected language
}} }}
className={`w-full text-left px-4 py-2 text-sm hover:bg-gray-50 transition-colors ${ className={`w-full text-left px-4 py-2 text-sm hover:bg-gray-50 transition-colors ${
selectedLanguage === lang.name currentLanguage === lang.code
? "bg-primary-50 text-primary-600 font-medium" ? "bg-primary-50 text-primary-600 font-medium"
: "text-gray-700" : "text-gray-700"
}`} }`}
@ -132,7 +125,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
className="flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 text-red-600 rounded-lg transition-colors border border-red-200" className="flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 text-red-600 rounded-lg transition-colors border border-red-200"
> >
<FiFlag className="w-4 h-4" /> <FiFlag className="w-4 h-4" />
<span className="font-medium">Report</span> <span className="font-medium">{t('common.report')}</span>
</button> </button>
)} )}
</div> </div>
@ -159,7 +152,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
</div> </div>
<div className="text-right flex-shrink-0"> <div className="text-right flex-shrink-0">
<p className="text-sm text-gray-600 mb-1 whitespace-nowrap"> <p className="text-sm text-gray-600 mb-1 whitespace-nowrap">
Requesting {t('requester.requesting')}
</p> </p>
<p className="text-xl sm:text-2xl font-bold text-primary-600 whitespace-nowrap"> <p className="text-xl sm:text-2xl font-bold text-primary-600 whitespace-nowrap">
{formatCurrency(requester.requestAmount)} {formatCurrency(requester.requestAmount)}
@ -184,10 +177,10 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
<div> <div>
<div className="text-left mb-6"> <div className="text-left mb-6">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words"> <h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words">
Choose recipient account {t('requester.chooseRecipient')}
</h2> </h2>
<p className="text-gray-600 text-sm md:text-base break-words"> <p className="text-gray-600 text-sm md:text-base break-words">
Select which account to send the money to {t('requester.chooseRecipientDesc')}
</p> </p>
</div> </div>
@ -237,10 +230,10 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
<div> <div>
<div className="text-left mb-6"> <div className="text-left mb-6">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words"> <h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words">
Recipient Account {t('requester.recipientAccount')}
</h2> </h2>
<p className="text-gray-600 text-sm md:text-base break-words"> <p className="text-gray-600 text-sm md:text-base break-words">
Money will be sent to this account {t('requester.recipientAccountDesc')}
</p> </p>
</div> </div>
@ -292,7 +285,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
}} }}
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-3 px-6 rounded-lg transition-colors" className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-3 px-6 rounded-lg transition-colors"
> >
Next Step {t('common.next')}
</button> </button>
)} )}
@ -303,7 +296,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
<div className="p-6"> <div className="p-6">
<div className="flex justify-between items-start mb-6"> <div className="flex justify-between items-start mb-6">
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900">
Report User {t('requester.reportUser')}
</h2> </h2>
<button <button
onClick={() => { onClick={() => {
@ -318,7 +311,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
<div className="mb-4"> <div className="mb-4">
<p className="text-sm text-gray-600 mb-4"> <p className="text-sm text-gray-600 mb-4">
Why are you reporting{" "} {t('requester.reportReason')}{" "}
<span className="font-medium">{requester.name}</span>? <span className="font-medium">{requester.name}</span>?
</p> </p>
<div className="space-y-3"> <div className="space-y-3">
@ -332,7 +325,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
className="w-4 h-4 text-primary-600" className="w-4 h-4 text-primary-600"
/> />
<span className="text-sm text-gray-900"> <span className="text-sm text-gray-900">
Spam or suspicious activity {t('requester.spam')}
</span> </span>
</label> </label>
<label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50"> <label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
@ -345,7 +338,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
className="w-4 h-4 text-primary-600" className="w-4 h-4 text-primary-600"
/> />
<span className="text-sm text-gray-900"> <span className="text-sm text-gray-900">
I don't know this person {t('requester.unknown')}
</span> </span>
</label> </label>
<label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50"> <label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
@ -357,7 +350,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
onChange={(e) => setFlagReason(e.target.value)} onChange={(e) => setFlagReason(e.target.value)}
className="w-4 h-4 text-primary-600" className="w-4 h-4 text-primary-600"
/> />
<span className="text-sm text-gray-900">Other</span> <span className="text-sm text-gray-900">{t('requester.other')}</span>
</label> </label>
</div> </div>
</div> </div>
@ -374,7 +367,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
disabled={!flagReason} disabled={!flagReason}
className="flex-1 bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="flex-1 bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
> >
Report {t('common.report')}
</button> </button>
<button <button
onClick={() => { onClick={() => {
@ -383,7 +376,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
}} }}
className="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors" className="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors"
> >
Cancel {t('common.cancel')}
</button> </button>
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Card } from '../ui/Card'; import { Card } from '../ui/Card';
import { Logo } from '../ui/Logo'; import { Logo } from '../ui/Logo';
import { useLanguage } from '../../contexts/LanguageContext';
import { FiCheckCircle, FiMail, FiSmartphone, FiDownload } from 'react-icons/fi'; import { FiCheckCircle, FiMail, FiSmartphone, FiDownload } from 'react-icons/fi';
interface SuccessPageProps { interface SuccessPageProps {
@ -17,6 +18,7 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
}) => { }) => {
const [emailSent, setEmailSent] = useState(false); const [emailSent, setEmailSent] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useLanguage();
const formatCurrency = (amount: number) => { const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('en-US', { return new Intl.NumberFormat('en-US', {
@ -53,21 +55,21 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
<FiCheckCircle className="w-12 h-12 text-primary-500" /> <FiCheckCircle className="w-12 h-12 text-primary-500" />
</div> </div>
<h1 className="text-3xl font-bold text-gray-900 mb-2"> <h1 className="text-3xl font-bold text-gray-900 mb-2">
Payment Successful! {t('success.title')}
</h1> </h1>
<p className="text-gray-600 mb-1"> <p className="text-gray-600 mb-1">
Your payment of {formatCurrency(amount)} has been processed successfully. {t('success.message').replace('{amount}', formatCurrency(amount))}
</p> </p>
<p className="text-sm text-gray-500">Transaction ID: {transactionId}</p> <p className="text-sm text-gray-500">{t('success.transactionId')}: {transactionId}</p>
</div> </div>
{/* Download App Section */} {/* Download App Section */}
<div className="mb-8"> <div className="mb-8">
<h2 className="text-xl font-bold text-gray-900 mb-4 text-center"> <h2 className="text-xl font-bold text-gray-900 mb-4 text-center">
Download the Amba App {t('success.downloadApp')}
</h2> </h2>
<p className="text-sm text-gray-600 text-center mb-6"> <p className="text-sm text-gray-600 text-center mb-6">
Get the Amba app to manage your payments and transactions on the go. {t('success.downloadDesc')}
</p> </p>
{/* Download Buttons */} {/* Download Buttons */}
@ -78,8 +80,8 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
> >
<FiSmartphone className="w-6 h-6" /> <FiSmartphone className="w-6 h-6" />
<div className="text-left"> <div className="text-left">
<p className="text-xs">Download on the</p> <p className="text-xs">{t('success.downloadAppStore')}</p>
<p className="font-semibold">App Store</p> <p className="font-semibold">{t('success.appStore')}</p>
</div> </div>
</button> </button>
<button <button
@ -88,8 +90,8 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
> >
<FiSmartphone className="w-6 h-6" /> <FiSmartphone className="w-6 h-6" />
<div className="text-left"> <div className="text-left">
<p className="text-xs">Get it on</p> <p className="text-xs">{t('success.downloadGooglePlay')}</p>
<p className="font-semibold">Google Play</p> <p className="font-semibold">{t('success.googlePlay')}</p>
</div> </div>
</button> </button>
</div> </div>
@ -100,7 +102,7 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<FiMail className="w-5 h-5 text-primary-500" /> <FiMail className="w-5 h-5 text-primary-500" />
<div> <div>
<p className="font-medium text-gray-900">Send download link via email</p> <p className="font-medium text-gray-900">{t('success.sendEmail')}</p>
<p className="text-sm text-gray-600">{email}</p> <p className="text-sm text-gray-600">{email}</p>
</div> </div>
</div> </div>
@ -109,12 +111,12 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
disabled={emailSent} disabled={emailSent}
className="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium" className="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium"
> >
{emailSent ? 'Sent!' : 'Send Link'} {emailSent ? t('success.sent') : t('success.sendLink')}
</button> </button>
</div> </div>
{emailSent && ( {emailSent && (
<div className="bg-green-50 border border-green-200 rounded-lg p-3 text-sm text-green-800"> <div className="bg-green-50 border border-green-200 rounded-lg p-3 text-sm text-green-800">
Download link has been sent to your email! {t('success.emailSent')}
</div> </div>
)} )}
</div> </div>
@ -126,14 +128,14 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
onClick={() => navigate('/')} onClick={() => navigate('/')}
className="flex-1 px-6 py-3 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors font-medium" className="flex-1 px-6 py-3 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors font-medium"
> >
Back to Home {t('success.backHome')}
</button> </button>
<button <button
onClick={() => window.print()} onClick={() => window.print()}
className="px-6 py-3 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors font-medium flex items-center gap-2" className="px-6 py-3 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors font-medium flex items-center gap-2"
> >
<FiDownload className="w-4 h-4" /> <FiDownload className="w-4 h-4" />
Download Receipt {t('success.downloadReceipt')}
</button> </button>
</div> </div>
</Card> </Card>

View File

@ -0,0 +1,574 @@
import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
export type LanguageCode = 'en' | 'fr' | 'am' | 'om' | 'ti';
interface Language {
code: LanguageCode;
name: string;
}
export const languages: Language[] = [
{ code: 'en', name: 'English' },
{ code: 'fr', name: 'French' },
{ code: 'am', name: 'Amharic' },
{ code: 'om', name: 'Oromo' },
{ code: 'ti', name: 'Tigrinya' },
];
interface LanguageContextType {
currentLanguage: LanguageCode;
setLanguage: (code: LanguageCode) => void;
t: (key: string) => string;
}
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
export const LanguageProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [currentLanguage, setCurrentLanguageState] = useState<LanguageCode>(() => {
// Get language from localStorage or default to English
const saved = localStorage.getItem('language') as LanguageCode;
return saved && languages.some(l => l.code === saved) ? saved : 'en';
});
useEffect(() => {
localStorage.setItem('language', currentLanguage);
}, [currentLanguage]);
const setLanguage = (code: LanguageCode) => {
setCurrentLanguageState(code);
};
// Translation function - will be implemented with actual translations
const t = (key: string): string => {
const translations = getTranslations(currentLanguage);
return translations[key] || key;
};
return (
<LanguageContext.Provider value={{ currentLanguage, setLanguage, t }}>
{children}
</LanguageContext.Provider>
);
};
export const useLanguage = () => {
const context = useContext(LanguageContext);
if (context === undefined) {
throw new Error('useLanguage must be used within a LanguageProvider');
}
return context;
};
// Translation data
const translations: Record<LanguageCode, Record<string, string>> = {
en: {
// Common
'common.next': 'Next Step',
'common.back': 'Back',
'common.cancel': 'Cancel',
'common.submit': 'Submit',
'common.loading': 'Loading...',
'common.email': 'Email',
'common.address': 'Address',
'common.name': 'Name',
'common.phone': 'Phone',
'common.amount': 'Amount',
'common.total': 'Total',
'common.subtotal': 'Subtotal',
'common.fee': 'Fee',
'common.donation': 'Donation',
'common.terms': 'Terms',
'common.privacy': 'Privacy Policy',
'common.report': 'Report',
'common.select': 'Select',
'common.continue': 'Continue',
'common.pay': 'Pay',
'common.processing': 'Processing...',
// Requester Details
'requester.paymentRequest': 'Payment Request',
'requester.chooseRecipient': 'Choose recipient account',
'requester.chooseRecipientDesc': 'Select which account to send the money to',
'requester.recipientAccount': 'Recipient Account',
'requester.recipientAccountDesc': 'Money will be sent to this account',
'requester.requesting': 'Requesting',
'requester.reportUser': 'Report User',
'requester.reportReason': 'Why are you reporting',
'requester.spam': 'Spam or suspicious activity',
'requester.unknown': "I don't know this person",
'requester.other': 'Other',
// Checkout
'checkout.paymentMethod': 'Payment Method',
'checkout.paymentTo': 'Payment to',
'checkout.completePayment': 'Complete your payment to proceed',
'checkout.totalAmount': 'Total amount',
'checkout.orderSummary': 'Order Summary',
'checkout.transactionFee': 'Transaction Fee',
'checkout.totalDue': 'Total due today',
'checkout.payWithCard': 'Pay With Card',
'checkout.cardDetails': 'Card details',
'checkout.cardNumber': 'Card Number',
'checkout.nameOnCard': 'Name on card',
'checkout.expiryDate': 'Expiry Date',
'checkout.cvc': 'CVC',
'checkout.billingAddress': 'Billing address',
'checkout.agreeTerms': 'By confirming your payment, you allow us to charge your card for this and future payments in accordance with terms. You can always cancel your subscription.',
'checkout.verifiedBy': 'Verified checkout page by',
'checkout.referralCode': 'Referral Code',
'checkout.enterCode': 'Enter code',
// Payment Methods
'payment.card': 'Card',
'payment.googlePay': 'Google Pay',
'payment.applePay': 'Apple Pay',
'payment.payWithGoogle': 'Pay with Google Pay',
'payment.payWithApple': 'Pay with Apple Pay',
// Donation
'donation.addDonation': 'Add donation to institution',
'donation.selectCause': 'Select Cause',
'donation.none': 'None',
'donation.noDonation': 'No donation',
// Success
'success.title': 'Payment Successful!',
'success.message': 'Your payment of {amount} has been processed successfully.',
'success.transactionId': 'Transaction ID',
'success.downloadApp': 'Download the Amba App',
'success.downloadDesc': 'Get the Amba app to manage your payments and transactions on the go.',
'success.downloadAppStore': 'Download on the',
'success.downloadGooglePlay': 'Get it on',
'success.appStore': 'App Store',
'success.googlePlay': 'Google Play',
'success.sendEmail': 'Send download link via email',
'success.sendLink': 'Send Link',
'success.sent': 'Sent!',
'success.emailSent': 'Download link has been sent to your email!',
'success.backHome': 'Back to Home',
'success.downloadReceipt': 'Download Receipt',
// Promotional Panel
'promo.title': 'Start searching for homes or posting ads now.',
'promo.description': 'Interfaces are well designed for all ages and target audiences are extremely simple and work with social media integrations',
'promo.downloadApp': 'Download the Amba App',
'promo.ios': 'iOS',
'promo.android': 'Android',
// Errors
'error.required': 'This field is required',
'error.invalidEmail': 'Invalid email address',
'error.invalidCard': 'Invalid card number',
'error.invalidExpiry': 'Invalid expiry date',
'error.invalidCvc': 'Invalid CVC',
},
fr: {
// Common
'common.next': 'Étape suivante',
'common.back': 'Retour',
'common.cancel': 'Annuler',
'common.submit': 'Soumettre',
'common.loading': 'Chargement...',
'common.email': 'E-mail',
'common.address': 'Adresse',
'common.name': 'Nom',
'common.phone': 'Téléphone',
'common.amount': 'Montant',
'common.total': 'Total',
'common.subtotal': 'Sous-total',
'common.fee': 'Frais',
'common.donation': 'Don',
'common.terms': 'Conditions',
'common.privacy': 'Politique de confidentialité',
'common.report': 'Signaler',
'common.select': 'Sélectionner',
'common.continue': 'Continuer',
'common.pay': 'Payer',
'common.processing': 'Traitement...',
// Requester Details
'requester.paymentRequest': 'Demande de paiement',
'requester.chooseRecipient': 'Choisir le compte du bénéficiaire',
'requester.chooseRecipientDesc': 'Sélectionnez le compte vers lequel envoyer l\'argent',
'requester.recipientAccount': 'Compte bénéficiaire',
'requester.recipientAccountDesc': 'L\'argent sera envoyé à ce compte',
'requester.requesting': 'Demande',
'requester.reportUser': 'Signaler l\'utilisateur',
'requester.reportReason': 'Pourquoi signalez-vous',
'requester.spam': 'Spam ou activité suspecte',
'requester.unknown': 'Je ne connais pas cette personne',
'requester.other': 'Autre',
// Checkout
'checkout.paymentMethod': 'Méthode de paiement',
'checkout.paymentTo': 'Paiement à',
'checkout.completePayment': 'Complétez votre paiement pour continuer',
'checkout.totalAmount': 'Montant total',
'checkout.orderSummary': 'Résumé de la commande',
'checkout.transactionFee': 'Frais de transaction',
'checkout.totalDue': 'Total dû aujourd\'hui',
'checkout.payWithCard': 'Payer par carte',
'checkout.cardDetails': 'Détails de la carte',
'checkout.cardNumber': 'Numéro de carte',
'checkout.nameOnCard': 'Nom sur la carte',
'checkout.expiryDate': 'Date d\'expiration',
'checkout.cvc': 'CVC',
'checkout.billingAddress': 'Adresse de facturation',
'checkout.agreeTerms': 'En confirmant votre paiement, vous nous autorisez à débiter votre carte pour ce paiement et les paiements futurs conformément aux conditions. Vous pouvez toujours annuler votre abonnement.',
'checkout.verifiedBy': 'Page de paiement vérifiée par',
'checkout.referralCode': 'Code de parrainage',
'checkout.enterCode': 'Entrer le code',
// Payment Methods
'payment.card': 'Carte',
'payment.googlePay': 'Google Pay',
'payment.applePay': 'Apple Pay',
'payment.payWithGoogle': 'Payer avec Google Pay',
'payment.payWithApple': 'Payer avec Apple Pay',
// Donation
'donation.addDonation': 'Ajouter un don à l\'institution',
'donation.selectCause': 'Sélectionner une cause',
'donation.none': 'Aucun',
'donation.noDonation': 'Pas de don',
// Success
'success.title': 'Paiement réussi!',
'success.message': 'Votre paiement de {amount} a été traité avec succès.',
'success.transactionId': 'ID de transaction',
'success.downloadApp': 'Télécharger l\'application Amba',
'success.downloadDesc': 'Obtenez l\'application Amba pour gérer vos paiements et transactions en déplacement.',
'success.downloadAppStore': 'Télécharger sur',
'success.downloadGooglePlay': 'Obtenir sur',
'success.appStore': 'App Store',
'success.googlePlay': 'Google Play',
'success.sendEmail': 'Envoyer le lien de téléchargement par e-mail',
'success.sendLink': 'Envoyer le lien',
'success.sent': 'Envoyé!',
'success.emailSent': 'Le lien de téléchargement a été envoyé à votre e-mail!',
'success.backHome': 'Retour à l\'accueil',
'success.downloadReceipt': 'Télécharger le reçu',
// Promotional Panel
'promo.title': 'Commencez à rechercher des maisons ou à publier des annonces maintenant.',
'promo.description': 'Les interfaces sont bien conçues pour tous les âges et les publics cibles sont extrêmement simples et fonctionnent avec des intégrations de médias sociaux',
'promo.downloadApp': 'Télécharger l\'application Amba',
'promo.ios': 'iOS',
'promo.android': 'Android',
// Errors
'error.required': 'Ce champ est requis',
'error.invalidEmail': 'Adresse e-mail invalide',
'error.invalidCard': 'Numéro de carte invalide',
'error.invalidExpiry': 'Date d\'expiration invalide',
'error.invalidCvc': 'CVC invalide',
},
am: {
// Common
'common.next': 'የሚቀጥለው ደረጃ',
'common.back': 'ተመለስ',
'common.cancel': 'ተወዳዳሪ',
'common.submit': 'ላክ',
'common.loading': 'በመጫን ላይ...',
'common.email': 'ኢሜይል',
'common.address': 'አድራሻ',
'common.name': 'ስም',
'common.phone': 'ስልክ',
'common.amount': 'መጠን',
'common.total': 'ጠቅላላ',
'common.subtotal': 'ንዑስ ጠቅላላ',
'common.fee': 'ክፍያ',
'common.donation': 'ልገሳ',
'common.terms': 'ውሎች',
'common.privacy': 'የግላዊነት ፖሊሲ',
'common.report': 'ሪፖርት',
'common.select': 'ምረጥ',
'common.continue': 'ቀጥል',
'common.pay': 'ክፍያ ይፈጽሙ',
'common.processing': 'በማቀናበር ላይ...',
// Requester Details
'requester.paymentRequest': 'የክፍያ ጥያቄ',
'requester.chooseRecipient': 'የተቀባዩን መለያ ይምረጡ',
'requester.chooseRecipientDesc': 'ገንዘቡን ወደ የትኛው መለያ እንደሚላኩ ይምረጡ',
'requester.recipientAccount': 'የተቀባይ መለያ',
'requester.recipientAccountDesc': 'ገንዘቡ ወደዚህ መለያ ይላካል',
'requester.requesting': 'ይጠይቃል',
'requester.reportUser': 'ተጠቃሚውን ሪፖርት ያድርጉ',
'requester.reportReason': 'ለምን ነው',
'requester.spam': 'ስፓም ወይም አጠራጣሪ እንቅስቃሴ',
'requester.unknown': 'ይህን ሰው አላውቅም',
'requester.other': 'ሌላ',
// Checkout
'checkout.paymentMethod': 'የክፍያ ዘዴ',
'checkout.paymentTo': 'ክፍያ ወደ',
'checkout.completePayment': 'ክፍያዎን ለመጠናቀቅ ይቀጥሉ',
'checkout.totalAmount': 'ጠቅላላ መጠን',
'checkout.orderSummary': 'የትዕዛዝ ማጠቃለያ',
'checkout.transactionFee': 'የግብይት ክፍያ',
'checkout.totalDue': 'ዛሬ የሚከፈለው ጠቅላላ',
'checkout.payWithCard': 'በካርድ ይክፈሉ',
'checkout.cardDetails': 'የካርድ ዝርዝሮች',
'checkout.cardNumber': 'የካርድ ቁጥር',
'checkout.nameOnCard': 'በካርድ ላይ ያለ ስም',
'checkout.expiryDate': 'የመጨረሻ ቀን',
'checkout.cvc': 'CVC',
'checkout.billingAddress': 'የክፍያ አድራሻ',
'checkout.agreeTerms': 'ክፍያዎን በማረጋገጥ ላይ ካርድዎን ለዚህ እና ለወደፊት ክፍያዎች በውሎች መሠረት እንድንክፈል እንፈቅድልናል። ማስተናገድዎን ሁልጊዜ ማቋረጥ ይችላሉ።',
'checkout.verifiedBy': 'የተረጋገጠ የክፍያ ገጽ በ',
'checkout.referralCode': 'የማጣቀሻ ኮድ',
'checkout.enterCode': 'ኮድ ያስገቡ',
// Payment Methods
'payment.card': 'ካርድ',
'payment.googlePay': 'ጉግል ፔይ',
'payment.applePay': 'አፕል ፔይ',
'payment.payWithGoogle': 'በጉግል ፔይ ይክፈሉ',
'payment.payWithApple': 'በአፕል ፔይ ይክፈሉ',
// Donation
'donation.addDonation': 'ለተቋም ልገሳ ያክሉ',
'donation.selectCause': 'ምክንያት ይምረጡ',
'donation.none': 'ምንም',
'donation.noDonation': 'ምንም ልገሳ የለም',
// Success
'success.title': 'ክፍያ በተሳካ ሁኔታ!',
'success.message': 'የ{amount} ክፍያዎ በተሳካ ሁኔታ ተከናውኗል።',
'success.transactionId': 'የግብይት መለያ',
'success.downloadApp': 'የአምባ መተግበሪያ ያውርዱ',
'success.downloadDesc': 'ክፍያዎችዎን እና ግብይቶችዎን በመንቀሳቀስ ላይ ለመስተዳደር የአምባ መተግበሪያ ያግኙ።',
'success.downloadAppStore': 'በ',
'success.downloadGooglePlay': 'ከ',
'success.appStore': 'አፕ ስቶር',
'success.googlePlay': 'ጉግል ፕሌይ',
'success.sendEmail': 'የማውረድ አገናኝ በኢሜይል ይላኩ',
'success.sendLink': 'አገናኝ ይላኩ',
'success.sent': 'ተልኳል!',
'success.emailSent': 'የማውረድ አገናኝ ወደ ኢሜይልዎ ተልኳል!',
'success.backHome': 'ወደ መነሻ ተመለስ',
'success.downloadReceipt': 'ደረሰኝ ያውርዱ',
// Promotional Panel
'promo.title': 'አሁን ቤቶችን መፈለግ ወይም ማስታወቂያዎችን ማስቀመጥ ይጀምሩ።',
'promo.description': 'መገናኛዎች ለሁሉም ዕድሜዎች በደንብ የተነደፉ እና የዓላማ ገጾች በጣም ቀላል ናቸው እና ከማህበራዊ ሚዲያ ውህደቶች ጋር ይሠራሉ',
'promo.downloadApp': 'የአምባ መተግበሪያ ያውርዱ',
'promo.ios': 'iOS',
'promo.android': 'Android',
// Errors
'error.required': 'ይህ መስክ ያስፈልጋል',
'error.invalidEmail': 'ልክ ያልሆነ ኢሜይል አድራሻ',
'error.invalidCard': 'ልክ ያልሆነ የካርድ ቁጥር',
'error.invalidExpiry': 'ልክ ያልሆነ የመጨረሻ ቀን',
'error.invalidCvc': 'ልክ ያልሆነ CVC',
},
om: {
// Common
'common.next': 'Darbii Itti Aanu',
'common.back': 'Deebi\'i',
'common.cancel': 'Haqiisi',
'common.submit': 'Ergi',
'common.loading': 'Bakka bu\'aa jira...',
'common.email': 'Imeelii',
'common.address': 'Teessoo',
'common.name': 'Maqaa',
'common.phone': 'Bilbila',
'common.amount': 'Hammam',
'common.total': 'Waliigala',
'common.subtotal': 'Waliigala gabaabaa',
'common.fee': 'Kaffaltii',
'common.donation': 'Kennaa',
'common.terms': 'Haala',
'common.privacy': 'Ilaalcha Iccitii',
'common.report': 'Odeessi',
'common.select': 'Filadhu',
'common.continue': 'Itti fufi',
'common.pay': 'Kaffalti',
'common.processing': 'Odeeffannoo jira...',
// Requester Details
'requester.paymentRequest': 'Gaaffii Kaffaltii',
'requester.chooseRecipient': 'Akkaawuntii qeebi\'aa filadhu',
'requester.chooseRecipientDesc': 'Akkaawuntii qarshii erguuf filadhu',
'requester.recipientAccount': 'Akkaawuntii Qeebi\'aa',
'requester.recipientAccountDesc': 'Qarshin kun akkaawuntii kana irratti ergama',
'requester.requesting': 'Gaafata',
'requester.reportUser': 'Fayyadamaa Odeessi',
'requester.reportReason': 'Maaliif',
'requester.spam': 'Spam ykn hojii shakkii',
'requester.unknown': 'Namni kun hin beeku',
'requester.other': 'Kan biraa',
// Checkout
'checkout.paymentMethod': 'Karaa Kaffaltii',
'checkout.paymentTo': 'Kaffalti gara',
'checkout.completePayment': 'Kaffalti keessan guutaa itti fufaa',
'checkout.totalAmount': 'Hammam Waliigala',
'checkout.orderSummary': 'Gabaabaa Ajaja',
'checkout.transactionFee': 'Kaffaltii Daldalaa',
'checkout.totalDue': 'Har\'a Kaffaltamaa',
'checkout.payWithCard': 'Kaardii Waliin Kaffalti',
'checkout.cardDetails': 'Odeeffannoo Kaardii',
'checkout.cardNumber': 'Lakkoofsi Kaardii',
'checkout.nameOnCard': 'Maqaan Kaardii Irratti',
'checkout.expiryDate': 'Guyyaa Xumuramaa',
'checkout.cvc': 'CVC',
'checkout.billingAddress': 'Teessoo Kaffaltii',
'checkout.agreeTerms': 'Kaffalti keessan mirkaneessanii, kaardii keessan kanaafi kaffaltiwwan kan itti aanuuf haala waliin kaffaltuu nuu dhiheessitan. Abbaandhala keessan yeroo hundumaa dhaamsiisuu ni dandeessan.',
'checkout.verifiedBy': 'Fuula kaffaltii mirkaneffame',
'checkout.referralCode': 'Koodii Gargaarsa',
'checkout.enterCode': 'Koodii Galchi',
// Payment Methods
'payment.card': 'Kaardii',
'payment.googlePay': 'Google Pay',
'payment.applePay': 'Apple Pay',
'payment.payWithGoogle': 'Google Pay Waliin Kaffalti',
'payment.payWithApple': 'Apple Pay Waliin Kaffalti',
// Donation
'donation.addDonation': 'Kennaa Hayyamaatti Dabaladhu',
'donation.selectCause': 'Sababa Filadhu',
'donation.none': 'Hin Qabne',
'donation.noDonation': 'Kennaa Hin Qabne',
// Success
'success.title': 'Kaffalti Milkaa\'e!',
'success.message': 'Kaffalti {amount} keessan milkaa\'inaan oggaamameera.',
'success.transactionId': 'ID Daldalaa',
'success.downloadApp': 'App Amba Buufadhu',
'success.downloadDesc': 'App Amba argadhu kaffaltiwwan keessaniifi daldalliwwan keessan deemsa keessatti to\'achuuf.',
'success.downloadAppStore': 'Buufadhu',
'success.downloadGooglePlay': 'Argadhu',
'success.appStore': 'App Store',
'success.googlePlay': 'Google Play',
'success.sendEmail': 'Linki Buufachuu Imeelii Ergi',
'success.sendLink': 'Linki Ergi',
'success.sent': 'Ergameera!',
'success.emailSent': 'Linki buufachuu imeelii keessan irratti ergameera!',
'success.backHome': 'Manaa Deebi\'i',
'success.downloadReceipt': 'Risiitii Buufadhu',
// Promotional Panel
'promo.title': 'Amma Mana Barbaaduu ykn Ogeessitoota Dhiyeessuu Eegali.',
'promo.description': 'Interfaces hundi umurri hundaaaf akka gaariitti ijaaramaniifi bakka maratni hundi baay\'ee salphaadhaafi media hawaasaa waliin hojjata',
'promo.downloadApp': 'App Amba Buufadhu',
'promo.ios': 'iOS',
'promo.android': 'Android',
// Errors
'error.required': 'Bakka kana barbaachisa',
'error.invalidEmail': 'Teessoo Imeelii Dogoggora',
'error.invalidCard': 'Lakkoofsi Kaardii Dogoggora',
'error.invalidExpiry': 'Guyyaa Xumuramaa Dogoggora',
'error.invalidCvc': 'CVC Dogoggora',
},
ti: {
// Common
'common.next': 'እታሕተዋይ ደረጃ',
'common.back': 'ተመለስ',
'common.cancel': 'ኣቅርብ',
'common.submit': 'ሰዲድ',
'common.loading': 'እየተጻወተ...',
'common.email': 'ኢ-መይል',
'common.address': 'ኣድራሻ',
'common.name': 'ስም',
'common.phone': 'ተሌፎን',
'common.amount': 'መጠን',
'common.total': 'ድምር',
'common.subtotal': 'ንእሽቶ ድምር',
'common.fee': 'ክፍሊት',
'common.donation': 'ልግሲ',
'common.terms': 'ስምምዕታት',
'common.privacy': 'ፖሊሲ ምስጢር',
'common.report': 'ሪፖርት',
'common.select': 'ምረጥ',
'common.continue': 'ቀጽል',
'common.pay': 'ክፍሊ',
'common.processing': 'እየተሰርሐ...',
// Requester Details
'requester.paymentRequest': 'ሕቶ ክፍሊት',
'requester.chooseRecipient': 'ናይ ተቐባሊ ኣካውንት ምረጥ',
'requester.chooseRecipientDesc': 'ነዳዲ ናብ ኣየነዳድ ኣካውንት ከም ዝውሃብ ምረጥ',
'requester.recipientAccount': 'ኣካውንት ተቐባሊ',
'requester.recipientAccountDesc': 'ነዳዲ ናብ እዚ ኣካውንት ክውሃብ እዩ',
'requester.requesting': 'ይሕትት',
'requester.reportUser': 'ተጠቃሚ ሪፖርት ግበር',
'requester.reportReason': 'ንምንታይ',
'requester.spam': 'ስፓም ወይ ጐዳይ ንጥፈታት',
'requester.unknown': 'እዚ ሰብ ኣይፈልጦን',
'requester.other': 'ካልእ',
// Checkout
'checkout.paymentMethod': 'ኣገባብ ክፍሊት',
'checkout.paymentTo': 'ክፍሊት ናብ',
'checkout.completePayment': 'ክፍሊትካ ንምውዳእ ቀጽል',
'checkout.totalAmount': 'ድምር መጠን',
'checkout.orderSummary': 'ጽማቝ ትእዛዝ',
'checkout.transactionFee': 'ክፍሊት ዕዋፍ',
'checkout.totalDue': 'ሎሚ ዝክፈል ድምር',
'checkout.payWithCard': 'ብካርድ ክፍሊ',
'checkout.cardDetails': 'ዝርዝር ካርድ',
'checkout.cardNumber': 'ቁጽሪ ካርድ',
'checkout.nameOnCard': 'ስም ኣብ ካርድ',
'checkout.expiryDate': 'ዕለት ዝዛዘም',
'checkout.cvc': 'CVC',
'checkout.billingAddress': 'ኣድራሻ ክፍሊት',
'checkout.agreeTerms': 'ክፍሊትካ ብምርግጋጽ ንዚእን ንዝመጽእ ክፍሊትታት ንካርድካ ኣብ ስምምዕታት መሰረት ክንክፍል ንፈቐድና። ምዝገባኻ ኩሉ ግዜ ክትቋርጽ ትኽእል ኢኻ።',
'checkout.verifiedBy': 'እተረጋገጸ ገጽ ክፍሊት ብ',
'checkout.referralCode': 'ኮድ ምላሽ',
'checkout.enterCode': 'ኮድ ኣእቱ',
// Payment Methods
'payment.card': 'ካርድ',
'payment.googlePay': 'ጉግል ፔይ',
'payment.applePay': 'ኣፕል ፔይ',
'payment.payWithGoogle': 'ብጉግል ፔይ ክፍሊ',
'payment.payWithApple': 'ብኣፕል ፔይ ክፍሊ',
// Donation
'donation.addDonation': 'ልግሲ ናብ ትካል ወስኽ',
'donation.selectCause': 'ምክንያት ምረጥ',
'donation.none': 'ኣይኮነን',
'donation.noDonation': 'ልግሲ የለን',
// Success
'success.title': 'ክፍሊት ተዓወተ!',
'success.message': 'ክፍሊት {amount}ኻ ብዓወት ተፈጺሙ።',
'success.transactionId': 'ID ዕዋፍ',
'success.downloadApp': 'ኣፕ ኣምባ ኣውርድ',
'success.downloadDesc': 'ክፍሊትታትኻን ዕዋፋትኻን ኣብ ምንቅስቓስ ንምቕዳሕ ኣፕ ኣምባ ረኸብ።',
'success.downloadAppStore': 'ኣውርድ',
'success.downloadGooglePlay': 'ረኸብ',
'success.appStore': 'ኣፕ ስቶር',
'success.googlePlay': 'ጉግል ፕሌይ',
'success.sendEmail': 'ሊንክ ምውራድ ብኢ-መይል ሰዲድ',
'success.sendLink': 'ሊንክ ሰዲድ',
'success.sent': 'ተሰዲዱ!',
'success.emailSent': 'ሊንክ ምውራድ ናብ ኢ-መይልኻ ተሰዲዱ!',
'success.backHome': 'ናብ ገዛ ተመለስ',
'success.downloadReceipt': 'ረሲት ኣውርድ',
// Promotional Panel
'promo.title': 'ኣሁን ገዛታት ምድላይ ወይ ማስታወሻታት ምስጢር ይጅምሩ።',
'promo.description': 'መገናኛታት ንኹሉ ዕድመ ብጽቡቕ ዝተነድፈ እዩ እሞ ዕላማ ሰባት ኣዝዩ ቀሊል እዩ እሞ ምስ ማሕበራዊ ሚድያ ውህደት ይሰርሕ',
'promo.downloadApp': 'ኣፕ ኣምባ ኣውርድ',
'promo.ios': 'iOS',
'promo.android': 'Android',
// Errors
'error.required': 'እዚ ቦታ የድሊ',
'error.invalidEmail': 'ዘይቅኑዕ ኢ-መይል ኣድራሻ',
'error.invalidCard': 'ዘይቅኑዕ ቁጽሪ ካርድ',
'error.invalidExpiry': 'ዘይቅኑዕ ዕለት ዝዛዘም',
'error.invalidCvc': 'ዘይቅኑዕ CVC',
},
};
function getTranslations(lang: LanguageCode): Record<string, string> {
return translations[lang] || translations.en;
}