mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-06-13 00:01:11 +00:00
feat(i18n): replace all hardcoded strings with translation calls
- Add translation keys for dashboard, send, receive, history, swap, pools, staking, lpStaking, fees, tokens, errors, validation, time, and context sections to types.ts and all 6 language files - Replace hardcoded Kurdish/Turkish strings in all wallet components with useTranslation() hook t() calls - Replace hardcoded strings in non-React files (crypto, utils, error-tracking, wallet-storage, contexts) with standalone translate() - Fix Turkish strings incorrectly used in Kurdish codebase
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pezkuwi-telegram-miniapp",
|
||||
"version": "1.0.189",
|
||||
"version": "1.0.190",
|
||||
"type": "module",
|
||||
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
|
||||
"author": "Pezkuwichain Team",
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { useTranslation } from '@/i18n';
|
||||
|
||||
type TargetChain = 'asset-hub' | 'people';
|
||||
|
||||
@@ -31,14 +32,14 @@ const TARGET_CHAINS: ChainInfo[] = [
|
||||
{
|
||||
id: 'asset-hub',
|
||||
name: 'Asset Hub',
|
||||
description: 'Ji bo PEZ veguheztin',
|
||||
description: 'fees.forTransfers',
|
||||
teyrchainId: 1000,
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
id: 'people',
|
||||
name: 'People Chain',
|
||||
description: 'Ji bo nasname',
|
||||
description: 'fees.forIdentity',
|
||||
teyrchainId: 1004,
|
||||
color: 'purple',
|
||||
},
|
||||
@@ -52,6 +53,7 @@ interface Props {
|
||||
export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
const { api, assetHubApi, peopleApi, address, keypair } = useWallet();
|
||||
const { hapticImpact, showAlert } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [targetChain, setTargetChain] = useState<TargetChain>('asset-hub');
|
||||
const [toRelay, setToRelay] = useState(false); // false = Relay→Teyrchain, true = Teyrchain→Relay
|
||||
@@ -142,7 +144,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
|
||||
const handleTeleport = async () => {
|
||||
if (!address || !keypair) {
|
||||
showAlert('Cizdan girêdayî nîne');
|
||||
showAlert(t('fees.walletNotConnected'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -151,18 +153,18 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
const sourceApi: any = toRelay ? (targetChain === 'asset-hub' ? assetHubApi : peopleApi) : api;
|
||||
|
||||
if (!sourceApi) {
|
||||
showAlert('API girêdayî nîne');
|
||||
showAlert(t('fees.apiNotConnected'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!amount || parseFloat(amount) <= 0) {
|
||||
showAlert('Mîqdarek rast binivîse');
|
||||
showAlert(t('fees.enterValidAmount'));
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceBalance = getSourceBalance();
|
||||
if (sourceBalance === '--') {
|
||||
showAlert('Zincîr girêdayî nîne');
|
||||
showAlert(t('fees.chainNotConnected'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,7 +172,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
const currentBalance = parseFloat(sourceBalance);
|
||||
|
||||
if (sendAmount > currentBalance) {
|
||||
showAlert('Bakiye têrê nake');
|
||||
showAlert(t('fees.insufficientBalance'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -282,7 +284,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
const xcmPallet = toRelay ? (sourceApi.tx as any).pezkuwiXcm : sourceApi.tx.xcmPallet;
|
||||
|
||||
if (!xcmPallet?.limitedTeleportAssets) {
|
||||
throw new Error('XCM pallet nehate dîtin');
|
||||
throw new Error(t('fees.xcmPalletNotFound'));
|
||||
}
|
||||
|
||||
const tx = xcmPallet.limitedTeleportAssets(
|
||||
@@ -306,7 +308,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMessage = 'Teleport neserketî';
|
||||
let errorMessage = t('fees.teleportFailed');
|
||||
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = sourceApi.registry.findMetaError(dispatchError.asModule);
|
||||
@@ -337,7 +339,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
setTxStatus('error');
|
||||
setIsTransferring(false);
|
||||
hapticImpact('heavy');
|
||||
showAlert(error instanceof Error ? error.message : 'Çewtiyekî çêbû');
|
||||
showAlert(error instanceof Error ? error.message : t('fees.errorOccurred'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -361,8 +363,8 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
<Fuel className="w-5 h-5 text-yellow-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">Fee Zêde Bike</h2>
|
||||
<p className="text-xs text-muted-foreground">HEZ teleport</p>
|
||||
<h2 className="text-lg font-semibold">{t('fees.title')}</h2>
|
||||
<p className="text-xs text-muted-foreground">{t('fees.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -377,27 +379,29 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
{txStatus === 'success' ? (
|
||||
<div className="py-8 text-center">
|
||||
<CheckCircle className="w-16 h-16 text-green-500 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-semibold mb-2">Serketî!</h3>
|
||||
<h3 className="text-xl font-semibold mb-2">{t('fees.success')}</h3>
|
||||
<p className="text-muted-foreground">
|
||||
{amount} HEZ bo {getDestName()} hate şandin
|
||||
{t('fees.sentTo', { amount, chain: getDestName() })}
|
||||
</p>
|
||||
</div>
|
||||
) : txStatus === 'error' ? (
|
||||
<div className="py-8 text-center">
|
||||
<AlertCircle className="w-16 h-16 text-red-500 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-semibold mb-2">Neserketî</h3>
|
||||
<h3 className="text-xl font-semibold mb-2">{t('fees.failed')}</h3>
|
||||
<button
|
||||
onClick={() => setTxStatus('idle')}
|
||||
className="mt-4 px-6 py-2 bg-muted rounded-lg"
|
||||
>
|
||||
Dîsa Biceribîne
|
||||
{t('fees.tryAgain')}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{/* Target Chain Selection */}
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">Zincîra Armanc</label>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
{t('fees.targetChain')}
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
{TARGET_CHAINS.map((chain) => (
|
||||
<button
|
||||
@@ -420,7 +424,9 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
}`}
|
||||
/>
|
||||
<div className="text-sm font-medium">{chain.name}</div>
|
||||
<div className="text-xs text-muted-foreground">{chain.description}</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{t(chain.description as any)}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -499,13 +505,15 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
targetChain === 'asset-hub' ? 'text-blue-400' : 'text-purple-400'
|
||||
}`}
|
||||
>
|
||||
{selectedChain.description} kêmî 0.1 HEZ tê pêşniyarkirin.
|
||||
{t('fees.minRecommended', { description: t(selectedChain.description as any) })}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Amount Input */}
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">Mîqdar (HEZ)</label>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
{t('fees.amountHez')}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.0001"
|
||||
@@ -537,7 +545,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
{/* Status Messages */}
|
||||
{txStatus === 'signing' && (
|
||||
<div className="p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
|
||||
<p className="text-yellow-400 text-sm">Danûstandinê îmze bikin...</p>
|
||||
<p className="text-yellow-400 text-sm">{t('fees.signing')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -545,7 +553,7 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
<div className="p-3 bg-blue-500/10 border border-blue-500/30 rounded-lg">
|
||||
<p className="text-blue-400 text-sm flex items-center gap-2">
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
XCM Teleport tê çêkirin...
|
||||
{t('fees.xcmTeleportPending')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -559,12 +567,12 @@ export function FundFeesModal({ isOpen, onClose }: Props) {
|
||||
{isTransferring ? (
|
||||
<>
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
{txStatus === 'signing' ? 'Tê îmzekirin...' : 'Tê çêkirin...'}
|
||||
{txStatus === 'signing' ? t('fees.signingButton') : t('fees.processing')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Fuel className="w-5 h-5" />
|
||||
Bo {getDestName()} Bişîne
|
||||
{t('fees.sendTo', { chain: getDestName() })}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useState, useEffect, useCallback } from 'react';
|
||||
import { X, Lock, Unlock, Users, AlertCircle, Loader2, Shield, TrendingUp } from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface StakingInfo {
|
||||
@@ -35,6 +36,7 @@ const UNITS = 1_000_000_000_000; // 10^12
|
||||
export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
const { api, keypair, address, balance } = useWallet();
|
||||
const { hapticImpact, hapticNotification, showAlert } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [stakingInfo, setStakingInfo] = useState<StakingInfo | null>(null);
|
||||
const [validators, setValidators] = useState<ValidatorInfo[]>([]);
|
||||
@@ -55,7 +57,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stakingPallet = api.query.staking as any;
|
||||
if (!stakingPallet) {
|
||||
setError('Staking palleti bulunamadı');
|
||||
setError(t('staking.palletNotFound'));
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -114,7 +116,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
setValidators(validatorList);
|
||||
} catch (err) {
|
||||
console.error('Error fetching staking info:', err);
|
||||
setError('Staking bilgileri alınamadı');
|
||||
setError(t('staking.fetchError'));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -165,7 +167,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Bond neserketî';
|
||||
let errorMsg = t('staking.bondFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}`;
|
||||
@@ -180,13 +182,13 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
});
|
||||
|
||||
hapticNotification('success');
|
||||
showAlert(`${bondAmount} HEZ stake kirin serketî!`);
|
||||
showAlert(t('staking.bondSuccess', { amount: bondAmount }));
|
||||
setBondAmount('');
|
||||
fetchStakingInfo();
|
||||
setActiveTab('status');
|
||||
} catch (err) {
|
||||
console.error('Bond error:', err);
|
||||
setError(err instanceof Error ? err.message : 'Bond neserketî');
|
||||
setError(err instanceof Error ? err.message : t('staking.bondFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
@@ -216,7 +218,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Nominate neserketî';
|
||||
let errorMsg = t('staking.nominateFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}`;
|
||||
@@ -231,12 +233,12 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
});
|
||||
|
||||
hapticNotification('success');
|
||||
showAlert(`${selectedValidators.length} validator nominate kirin serketî!`);
|
||||
showAlert(t('staking.nominateSuccess', { count: selectedValidators.length }));
|
||||
fetchStakingInfo();
|
||||
setActiveTab('status');
|
||||
} catch (err) {
|
||||
console.error('Nominate error:', err);
|
||||
setError(err instanceof Error ? err.message : 'Nominate neserketî');
|
||||
setError(err instanceof Error ? err.message : t('staking.nominateFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
@@ -268,7 +270,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Unbond neserketî';
|
||||
let errorMsg = t('staking.unbondFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = api.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}`;
|
||||
@@ -283,13 +285,13 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
});
|
||||
|
||||
hapticNotification('success');
|
||||
showAlert(`${unbondAmount} HEZ unbond kirin serketî! (28 roj li bendê)`);
|
||||
showAlert(t('staking.unbondSuccess', { amount: unbondAmount }));
|
||||
setUnbondAmount('');
|
||||
fetchStakingInfo();
|
||||
setActiveTab('status');
|
||||
} catch (err) {
|
||||
console.error('Unbond error:', err);
|
||||
setError(err instanceof Error ? err.message : 'Unbond neserketî');
|
||||
setError(err instanceof Error ? err.message : t('staking.unbondFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
@@ -330,10 +332,10 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1 bg-secondary/50 rounded-lg p-1 mb-4">
|
||||
{[
|
||||
{ id: 'status' as const, label: 'Durum', icon: TrendingUp },
|
||||
{ id: 'bond' as const, label: 'Bond', icon: Lock },
|
||||
{ id: 'nominate' as const, label: 'Nominate', icon: Users },
|
||||
{ id: 'unbond' as const, label: 'Unbond', icon: Unlock },
|
||||
{ id: 'status' as const, label: t('staking.statusTab'), icon: TrendingUp },
|
||||
{ id: 'bond' as const, label: t('staking.bondTab'), icon: Lock },
|
||||
{ id: 'nominate' as const, label: t('staking.nominateTab'), icon: Users },
|
||||
{ id: 'unbond' as const, label: t('staking.unbondTab'), icon: Unlock },
|
||||
].map(({ id, label, icon: Icon }) => (
|
||||
<button
|
||||
key={id}
|
||||
@@ -368,7 +370,9 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
{stakingInfo ? (
|
||||
<>
|
||||
<div className="bg-gradient-to-r from-green-500/20 to-emerald-500/20 border border-green-500/30 rounded-xl p-4">
|
||||
<div className="text-sm text-muted-foreground mb-1">Aktîf Stake</div>
|
||||
<div className="text-sm text-muted-foreground mb-1">
|
||||
{t('staking.activeStake')}
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-green-400">
|
||||
{formatHEZ(stakingInfo.active)} HEZ
|
||||
</div>
|
||||
@@ -376,20 +380,24 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
|
||||
<div className="bg-secondary/50 rounded-xl p-4 space-y-3">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Giştî Bonded</span>
|
||||
<span className="text-muted-foreground">{t('staking.totalBonded')}</span>
|
||||
<span>{formatHEZ(stakingInfo.totalBonded)} HEZ</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Nominations</span>
|
||||
<span className="text-muted-foreground">{t('staking.nominations')}</span>
|
||||
<span>{stakingInfo.nominations.length} validator</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Xelat Armanc</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t('staking.rewardDestination')}
|
||||
</span>
|
||||
<span>{stakingInfo.rewardDestination}</span>
|
||||
</div>
|
||||
{stakingInfo.unlocking.length > 0 && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Unbonding</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t('staking.unbondingChunks')}
|
||||
</span>
|
||||
<span className="text-yellow-400">
|
||||
{stakingInfo.unlocking.length} chunk
|
||||
</span>
|
||||
@@ -400,7 +408,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
{stakingInfo.nominations.length > 0 && (
|
||||
<div className="bg-secondary/50 rounded-xl p-4">
|
||||
<div className="text-sm text-muted-foreground mb-2">
|
||||
Nominated Validators
|
||||
{t('staking.nominatedValidators')}
|
||||
</div>
|
||||
<div className="space-y-1 max-h-32 overflow-y-auto">
|
||||
{stakingInfo.nominations.map((addr, i) => (
|
||||
@@ -413,24 +421,21 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
)}
|
||||
|
||||
<div className="p-3 bg-blue-500/10 border border-blue-500/30 rounded-lg">
|
||||
<p className="text-blue-400 text-xs">
|
||||
💡 HEZ stake kirin Trust Score zêde dike. Herî kêm 1 meh stake bikî ji bo
|
||||
bonusê.
|
||||
</p>
|
||||
<p className="text-blue-400 text-xs">{t('staking.stakingTip')}</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<Shield className="w-16 h-16 text-muted-foreground mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium mb-2">Hîn Stake Nekiriye</h3>
|
||||
<h3 className="text-lg font-medium mb-2">{t('staking.notStakedYet')}</h3>
|
||||
<p className="text-muted-foreground text-sm mb-4">
|
||||
HEZ stake bike ji bo Trust Score qezenckirin
|
||||
{t('staking.stakeForTrustScore')}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setActiveTab('bond')}
|
||||
className="px-6 py-2 bg-green-600 text-white rounded-lg text-sm"
|
||||
>
|
||||
Dest Pê Bike
|
||||
{t('staking.startStaking')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -442,19 +447,23 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
<div className="space-y-4">
|
||||
<div className="bg-secondary/50 rounded-xl p-4">
|
||||
<div className="flex justify-between text-sm mb-2">
|
||||
<span className="text-muted-foreground">Bakiyê Te</span>
|
||||
<span className="text-muted-foreground">{t('staking.yourBalance')}</span>
|
||||
<span>{balance || '0'} HEZ</span>
|
||||
</div>
|
||||
{stakingInfo && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Niha Staked</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t('staking.currentlyStaked')}
|
||||
</span>
|
||||
<span className="text-green-400">{formatHEZ(stakingInfo.active)} HEZ</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">Mîqdar (HEZ)</label>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
{t('staking.amountHez')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="number"
|
||||
@@ -473,9 +482,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
|
||||
<p className="text-yellow-400 text-xs">
|
||||
⚠️ Stake kirinê paşê 28 roj li bendê ye ji bo vekişandinê.
|
||||
</p>
|
||||
<p className="text-yellow-400 text-xs">{t('staking.bondWarning')}</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -488,7 +495,11 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
) : (
|
||||
<Lock className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê bond kirin...' : stakingInfo ? 'Zêde Bike' : 'Bond Bike'}
|
||||
{isProcessing
|
||||
? t('staking.bonding')
|
||||
: stakingInfo
|
||||
? t('staking.bondExtra')
|
||||
: t('staking.bondButton')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -499,14 +510,12 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
{!stakingInfo ? (
|
||||
<div className="text-center py-8">
|
||||
<AlertCircle className="w-12 h-12 text-yellow-400 mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">
|
||||
Pêşî HEZ bond bike, paşê nominate bike
|
||||
</p>
|
||||
<p className="text-muted-foreground">{t('staking.bondFirst')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-sm text-muted-foreground mb-2">
|
||||
Validator hilbijêre (max 16): {selectedValidators.length}/16
|
||||
{t('staking.selectValidators', { count: selectedValidators.length })}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||
@@ -525,7 +534,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
{v.address.slice(0, 16)}...{v.address.slice(-8)}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Komîsyon: {v.commission.toFixed(2)}%
|
||||
{t('staking.commission')} {v.commission.toFixed(2)}%
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
@@ -541,7 +550,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
) : (
|
||||
<Users className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê nominate kirin...' : 'Nominate Bike'}
|
||||
{isProcessing ? t('staking.nominating') : t('staking.nominateButton')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
@@ -554,13 +563,13 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
{!stakingInfo ? (
|
||||
<div className="text-center py-8">
|
||||
<AlertCircle className="w-12 h-12 text-yellow-400 mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">Tu hîn stake nekiriye</p>
|
||||
<p className="text-muted-foreground">{t('staking.notStakedUnbond')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="bg-secondary/50 rounded-xl p-4">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">Aktîf Stake</span>
|
||||
<span className="text-muted-foreground">{t('staking.activeStake')}</span>
|
||||
<span className="text-green-400">
|
||||
{formatHEZ(stakingInfo.active)} HEZ
|
||||
</span>
|
||||
@@ -569,7 +578,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
Mîqdar (HEZ)
|
||||
{t('staking.amountHez')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
@@ -589,9 +598,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-orange-500/10 border border-orange-500/30 rounded-lg">
|
||||
<p className="text-orange-400 text-xs">
|
||||
⚠️ Unbond kirin 28 roj digire. Paşê dikare vekişîne.
|
||||
</p>
|
||||
<p className="text-orange-400 text-xs">{t('staking.unbondWarning')}</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -604,7 +611,7 @@ export function HEZStakingModal({ isOpen, onClose }: HEZStakingModalProps) {
|
||||
) : (
|
||||
<Unlock className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê unbond kirin...' : 'Unbond Bike'}
|
||||
{isProcessing ? t('staking.unbondProcessing') : t('staking.unbondButton')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useState, useEffect } from 'react';
|
||||
import { X, Lock, Unlock, Gift, AlertCircle, Loader2 } from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface StakingPool {
|
||||
@@ -56,6 +57,7 @@ function formatAssetLocation(assetId: number): object {
|
||||
export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
const { assetHubApi, keypair, address } = useWallet();
|
||||
const { hapticImpact, hapticNotification, showAlert } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [pools, setPools] = useState<StakingPool[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -76,7 +78,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
const poolEntries = await (assetHubApi.query.assetRewards as any)?.pools?.entries();
|
||||
|
||||
if (!poolEntries) {
|
||||
setError('Staking palleti amade nîne');
|
||||
setError(t('lpStaking.palletNotReady'));
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -170,7 +172,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching staking pools:', err);
|
||||
setError('Staking pools bar nekirin');
|
||||
setError(t('lpStaking.poolsNotLoaded'));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -212,7 +214,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Stake neserketî';
|
||||
let errorMsg = t('lpStaking.stakeFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}`;
|
||||
@@ -227,14 +229,14 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
});
|
||||
|
||||
hapticNotification('success');
|
||||
showAlert('Stake serket!');
|
||||
showAlert(t('lpStaking.stakeSuccess'));
|
||||
setStakeAmount('');
|
||||
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
}, 1500);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Stake neserketî');
|
||||
setError(err instanceof Error ? err.message : t('lpStaking.stakeFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
@@ -266,7 +268,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Unstake neserketî';
|
||||
let errorMsg = t('lpStaking.unstakeFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}`;
|
||||
@@ -281,14 +283,14 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
});
|
||||
|
||||
hapticNotification('success');
|
||||
showAlert('Unstake serket!');
|
||||
showAlert(t('lpStaking.unstakeSuccess'));
|
||||
setUnstakeAmount('');
|
||||
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
}, 1500);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Unstake neserketî');
|
||||
setError(err instanceof Error ? err.message : t('lpStaking.unstakeFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
@@ -318,7 +320,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
}) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Xelat stendin neserketî';
|
||||
let errorMsg = t('lpStaking.claimFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}`;
|
||||
@@ -333,13 +335,13 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
});
|
||||
|
||||
hapticNotification('success');
|
||||
showAlert('Xelat hat stendin!');
|
||||
showAlert(t('lpStaking.claimSuccess'));
|
||||
|
||||
setTimeout(() => {
|
||||
onClose();
|
||||
}, 1500);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Xelat stendin neserketî');
|
||||
setError(err instanceof Error ? err.message : t('lpStaking.claimFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
@@ -370,13 +372,15 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
) : pools.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<AlertCircle className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">Hêj staking pool tune ne</p>
|
||||
<p className="text-muted-foreground">{t('lpStaking.noPoolsYet')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Pool Selector */}
|
||||
<div className="mb-4">
|
||||
<label className="text-sm text-muted-foreground mb-2 block">Pool Hilbijêre</label>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
{t('lpStaking.selectPool')}
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{pools.map((pool) => (
|
||||
<button
|
||||
@@ -403,21 +407,21 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
<div className="bg-secondary/50 rounded-xl p-4 mb-4 border border-border">
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<div className="text-muted-foreground">Giştî Staked</div>
|
||||
<div className="text-muted-foreground">{t('lpStaking.totalStaked')}</div>
|
||||
<div className="font-medium">{formatAmount(currentPool.totalStaked)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">Te Stake Kiriye</div>
|
||||
<div className="text-muted-foreground">{t('lpStaking.youStaked')}</div>
|
||||
<div className="font-medium text-green-400">
|
||||
{formatAmount(currentPool.userStaked)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">LP Bakiye</div>
|
||||
<div className="text-muted-foreground">{t('lpStaking.lpBalance')}</div>
|
||||
<div className="font-medium">{formatAmount(currentPool.lpBalance)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">Xelat</div>
|
||||
<div className="text-muted-foreground">{t('lpStaking.reward')}</div>
|
||||
<div className="font-medium text-yellow-400">
|
||||
{formatAmount(currentPool.pendingRewards)} PEZ
|
||||
</div>
|
||||
@@ -429,9 +433,9 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1 bg-secondary/50 rounded-lg p-1 mb-4">
|
||||
{[
|
||||
{ id: 'stake' as const, label: 'Stake', icon: Lock },
|
||||
{ id: 'unstake' as const, label: 'Unstake', icon: Unlock },
|
||||
{ id: 'rewards' as const, label: 'Xelat', icon: Gift },
|
||||
{ id: 'stake' as const, label: t('lpStaking.stakeTab'), icon: Lock },
|
||||
{ id: 'unstake' as const, label: t('lpStaking.unstakeTab'), icon: Unlock },
|
||||
{ id: 'rewards' as const, label: t('lpStaking.rewardTab'), icon: Gift },
|
||||
].map(({ id, label, icon: Icon }) => (
|
||||
<button
|
||||
key={id}
|
||||
@@ -464,7 +468,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
Mîqdar ({currentPool.stakedAsset})
|
||||
{t('lpStaking.amount', { asset: currentPool.stakedAsset })}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
@@ -482,7 +486,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Bakiye: {formatAmount(currentPool.lpBalance)}
|
||||
{t('lpStaking.balanceLabel')} {formatAmount(currentPool.lpBalance)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -495,7 +499,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
) : (
|
||||
<Lock className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê stake kirin...' : 'Stake Bike'}
|
||||
{isProcessing ? t('lpStaking.staking') : t('lpStaking.stakeButton')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -505,7 +509,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm text-muted-foreground mb-2 block">
|
||||
Mîqdar ({currentPool.stakedAsset})
|
||||
{t('lpStaking.amount', { asset: currentPool.stakedAsset })}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
@@ -523,7 +527,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Staked: {formatAmount(currentPool.userStaked)}
|
||||
{t('lpStaking.stakedLabel')} {formatAmount(currentPool.userStaked)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -536,7 +540,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
) : (
|
||||
<Unlock className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê unstake kirin...' : 'Unstake Bike'}
|
||||
{isProcessing ? t('lpStaking.unstaking') : t('lpStaking.unstakeButton')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -549,7 +553,9 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
<div className="text-2xl font-bold text-yellow-400 mb-1">
|
||||
{formatAmount(currentPool.pendingRewards)} PEZ
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">Xelatên li bendê</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{t('lpStaking.pendingRewards')}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleClaimRewards}
|
||||
@@ -561,7 +567,7 @@ export function LPStakingModal({ isOpen, onClose }: LPStakingModalProps) {
|
||||
) : (
|
||||
<Gift className="w-5 h-5" />
|
||||
)}
|
||||
{isProcessing ? 'Tê stendin...' : 'Xelatan Bistîne'}
|
||||
{isProcessing ? t('lpStaking.claiming') : t('lpStaking.claimButton')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { X, Droplets, Plus, Minus, AlertCircle, Check } from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { KurdistanSun } from '@/components/KurdistanSun';
|
||||
import { useTranslation } from '@/i18n';
|
||||
|
||||
interface PoolsModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -52,6 +53,7 @@ const formatAssetLocation = (id: number) => {
|
||||
export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
const { assetHubApi, keypair } = useWallet();
|
||||
const { hapticImpact, hapticNotification } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [pools, setPools] = useState<Pool[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -267,7 +269,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch pools:', err);
|
||||
if (!isCancelled) {
|
||||
setError('Bağlantı hatası - tekrar deneyin');
|
||||
setError(t('pools.connectionError'));
|
||||
}
|
||||
} finally {
|
||||
if (!isCancelled) {
|
||||
@@ -331,7 +333,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
({ status, dispatchError }: { status: any; dispatchError: any }) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Zêdekirin neserketî';
|
||||
let errorMsg = t('pools.addFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`;
|
||||
@@ -349,7 +351,12 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
});
|
||||
|
||||
setSuccessMessage(
|
||||
`${amount0} ${selectedPool.asset0Symbol} + ${amount1} ${selectedPool.asset1Symbol} hate zêdekirin`
|
||||
t('pools.addedLiquidity', {
|
||||
amount0,
|
||||
token0: selectedPool.asset0Symbol,
|
||||
amount1,
|
||||
token1: selectedPool.asset1Symbol,
|
||||
})
|
||||
);
|
||||
setSuccess(true);
|
||||
hapticNotification('success');
|
||||
@@ -363,7 +370,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Add liquidity failed:', err);
|
||||
setError(err instanceof Error ? err.message : 'Zêdekirin neserketî');
|
||||
setError(err instanceof Error ? err.message : t('pools.addFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
@@ -376,7 +383,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
|
||||
const lpAmount = parseFloat(lpAmountToRemove);
|
||||
if (lpAmount <= 0 || lpAmount > (selectedPool.userLpBalance || 0)) {
|
||||
setError('Mîqdara LP ne derbasdar e');
|
||||
setError(t('pools.invalidLpAmount'));
|
||||
hapticNotification('error');
|
||||
return;
|
||||
}
|
||||
@@ -420,7 +427,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
({ status, dispatchError }: { status: any; dispatchError: any }) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Derxistin neserketî';
|
||||
let errorMsg = t('pools.removeFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`;
|
||||
@@ -437,7 +444,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
).catch(reject);
|
||||
});
|
||||
|
||||
setSuccessMessage(`${lpAmountToRemove} LP token hate vegerandin`);
|
||||
setSuccessMessage(t('pools.removedLiquidity', { amount: lpAmountToRemove }));
|
||||
setSuccess(true);
|
||||
hapticNotification('success');
|
||||
|
||||
@@ -449,7 +456,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Remove liquidity failed:', err);
|
||||
setError(err instanceof Error ? err.message : 'Derxistin neserketî');
|
||||
setError(err instanceof Error ? err.message : t('pools.removeFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
@@ -466,7 +473,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
<div className="w-16 h-16 mx-auto bg-green-500/20 rounded-full flex items-center justify-center">
|
||||
<Check className="w-8 h-8 text-green-500" />
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold">Serketî!</h2>
|
||||
<h2 className="text-xl font-semibold">{t('pools.success')}</h2>
|
||||
<p className="text-muted-foreground">{successMessage}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -489,9 +496,9 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
}}
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
← Paş
|
||||
{t('pools.back')}
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold">Liquidity Zêde Bike</h2>
|
||||
<h2 className="text-lg font-semibold">{t('pools.addLiquidity')}</h2>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
|
||||
@@ -515,7 +522,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">{selectedPool.asset0Symbol} Mîqdar</span>
|
||||
<span className="text-muted-foreground">
|
||||
Bakiye: {balances[selectedPool.asset0Symbol]}
|
||||
{t('swap.balanceLabel')} {balances[selectedPool.asset0Symbol]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
@@ -543,10 +550,10 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">
|
||||
{selectedPool.asset1Symbol} Mîqdar (otomatîk)
|
||||
{t('pools.amountAuto', { token: selectedPool.asset1Symbol })}
|
||||
</span>
|
||||
<span className="text-muted-foreground">
|
||||
Bakiye: {balances[selectedPool.asset1Symbol]}
|
||||
{t('swap.balanceLabel')} {balances[selectedPool.asset1Symbol]}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@@ -570,7 +577,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{isSubmitting ? (
|
||||
<div className="flex flex-col items-center justify-center py-4 space-y-3">
|
||||
<KurdistanSun size={80} />
|
||||
<p className="text-sm text-muted-foreground animate-pulse">Tê zêdekirin...</p>
|
||||
<p className="text-sm text-muted-foreground animate-pulse">{t('pools.adding')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
@@ -579,7 +586,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
className="w-full py-4 bg-gradient-to-r from-green-600 to-blue-600 text-white font-semibold rounded-xl disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
<Droplets className="w-5 h-5" />
|
||||
Liquidity Zêde Bike
|
||||
{t('pools.addLiquidity')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -603,9 +610,9 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
}}
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
← Paş
|
||||
{t('pools.back')}
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold">Liquidity Derxe</h2>
|
||||
<h2 className="text-lg font-semibold">{t('pools.removeLiquidity')}</h2>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
|
||||
@@ -617,7 +624,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-center text-sm text-muted-foreground mt-1">
|
||||
LP Bakiye: {selectedPool.userLpBalance?.toFixed(4) || '0'} LP
|
||||
{t('pools.lpBalance')} {selectedPool.userLpBalance?.toFixed(4) || '0'} LP
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -626,7 +633,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{/* LP Amount */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-muted-foreground">LP Token Mîqdar</span>
|
||||
<span className="text-muted-foreground">{t('pools.lpTokenAmount')}</span>
|
||||
<span className="text-muted-foreground">
|
||||
Max: {selectedPool.userLpBalance?.toFixed(4) || '0'}
|
||||
</span>
|
||||
@@ -651,7 +658,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{/* Estimated Returns */}
|
||||
{lpAmountToRemove && parseFloat(lpAmountToRemove) > 0 && (
|
||||
<div className="bg-muted/50 rounded-xl p-3 space-y-2 text-sm">
|
||||
<p className="text-muted-foreground">Texmînî vegerandin:</p>
|
||||
<p className="text-muted-foreground">{t('pools.estimatedReturn')}</p>
|
||||
<div className="flex justify-between">
|
||||
<span>{selectedPool.asset0Symbol}</span>
|
||||
<span className="font-mono">
|
||||
@@ -691,7 +698,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{isSubmitting ? (
|
||||
<div className="flex flex-col items-center justify-center py-4 space-y-3">
|
||||
<KurdistanSun size={80} />
|
||||
<p className="text-sm text-muted-foreground animate-pulse">Tê derxistin...</p>
|
||||
<p className="text-sm text-muted-foreground animate-pulse">{t('pools.removing')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
@@ -700,7 +707,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
className="w-full py-4 bg-gradient-to-r from-red-600 to-orange-600 text-white font-semibold rounded-xl disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
<Minus className="w-5 h-5" />
|
||||
Liquidity Derxe
|
||||
{t('pools.removeLiquidity')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -715,7 +722,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
<div className="w-full max-w-md bg-card rounded-2xl shadow-xl border border-border overflow-hidden max-h-[90vh] overflow-y-auto">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-border">
|
||||
<h2 className="text-lg font-semibold">Liquidity Pools</h2>
|
||||
<h2 className="text-lg font-semibold">{t('pools.title')}</h2>
|
||||
<button onClick={onClose} className="p-2 rounded-full hover:bg-muted">
|
||||
<X className="w-5 h-5 text-muted-foreground" />
|
||||
</button>
|
||||
@@ -726,12 +733,12 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{isLoading ? (
|
||||
<div className="flex flex-col items-center justify-center py-8">
|
||||
<KurdistanSun size={80} />
|
||||
<p className="text-muted-foreground mt-3 animate-pulse">Tê barkirin...</p>
|
||||
<p className="text-muted-foreground mt-3 animate-pulse">{t('pools.loadingPools')}</p>
|
||||
</div>
|
||||
) : pools.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<Droplets className="w-12 h-12 mx-auto text-muted-foreground mb-2" />
|
||||
<p className="text-muted-foreground">Pool tune</p>
|
||||
<p className="text-muted-foreground">{t('pools.noPools')}</p>
|
||||
</div>
|
||||
) : (
|
||||
pools.map((pool) => (
|
||||
@@ -760,13 +767,17 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{/* Pool Stats */}
|
||||
<div className="grid grid-cols-2 gap-2 text-sm mb-3">
|
||||
<div>
|
||||
<span className="text-muted-foreground">Rezerv {pool.asset0Symbol}</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t('pools.reserve')} {pool.asset0Symbol}
|
||||
</span>
|
||||
<p className="font-mono">
|
||||
{pool.reserve0.toLocaleString('en-US', { maximumFractionDigits: 0 })}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">Rezerv {pool.asset1Symbol}</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t('pools.reserve')} {pool.asset1Symbol}
|
||||
</span>
|
||||
<p className="font-mono">
|
||||
{pool.reserve1.toLocaleString('en-US', { maximumFractionDigits: 0 })}
|
||||
</p>
|
||||
@@ -777,7 +788,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
{pool.userLpBalance && pool.userLpBalance > 0 && (
|
||||
<div className="bg-green-500/10 border border-green-500/30 rounded-lg p-2 mb-3 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-green-400">Pozîsyona Te</span>
|
||||
<span className="text-green-400">{t('pools.yourPosition')}</span>
|
||||
<span className="text-green-400 font-mono">
|
||||
{pool.userShare?.toFixed(2)}%
|
||||
</span>
|
||||
@@ -799,7 +810,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
className="flex-1 py-2 bg-gradient-to-r from-green-600/20 to-blue-600/20 border border-green-500/30 text-green-400 font-medium rounded-lg flex items-center justify-center gap-1 text-sm"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Zêde Bike
|
||||
{t('pools.addButton')}
|
||||
</button>
|
||||
{pool.userLpBalance && pool.userLpBalance > 0 && (
|
||||
<button
|
||||
@@ -811,7 +822,7 @@ export function PoolsModal({ isOpen, onClose }: PoolsModalProps) {
|
||||
className="flex-1 py-2 bg-gradient-to-r from-red-600/20 to-orange-600/20 border border-red-500/30 text-red-400 font-medium rounded-lg flex items-center justify-center gap-1 text-sm"
|
||||
>
|
||||
<Minus className="w-4 h-4" />
|
||||
Derxe
|
||||
{t('pools.removeButton')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { X, ArrowDownUp, RefreshCw, AlertCircle, Check } from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { KurdistanSun } from '@/components/KurdistanSun';
|
||||
import { useTranslation } from '@/i18n';
|
||||
|
||||
interface SwapModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -36,6 +37,7 @@ const formatAssetLocation = (id: number) => {
|
||||
export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
const { assetHubApi, keypair } = useWallet();
|
||||
const { hapticImpact, hapticNotification } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [fromToken, setFromToken] = useState(TOKENS[0]); // HEZ
|
||||
const [toToken, setToToken] = useState(TOKENS[1]); // PEZ
|
||||
@@ -218,7 +220,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
const swapAmount = parseFloat(fromAmount);
|
||||
|
||||
if (swapAmount > fromBalance) {
|
||||
setError('Bakiye têrê nake');
|
||||
setError(t('swap.insufficientBalance'));
|
||||
hapticNotification('error');
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +264,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
({ status, dispatchError }: { status: any; dispatchError: any }) => {
|
||||
if (status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMsg = 'Swap neserketî';
|
||||
let errorMsg = t('swap.swapFailed');
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = assetHubApi.registry.findMetaError(dispatchError.asModule);
|
||||
errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`;
|
||||
@@ -291,7 +293,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Swap failed:', err);
|
||||
setError(err instanceof Error ? err.message : 'Swap neserketî');
|
||||
setError(err instanceof Error ? err.message : t('swap.swapFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsSwapping(false);
|
||||
@@ -307,7 +309,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
<div className="w-16 h-16 mx-auto bg-green-500/20 rounded-full flex items-center justify-center">
|
||||
<Check className="w-8 h-8 text-green-500" />
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold">Swap Serketî!</h2>
|
||||
<h2 className="text-xl font-semibold">{t('swap.swapSuccess')}</h2>
|
||||
<p className="text-muted-foreground">
|
||||
{fromAmount} {fromToken.symbol} → {toAmount} {toToken.symbol}
|
||||
</p>
|
||||
@@ -321,7 +323,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
<div className="w-full max-w-md bg-card rounded-2xl shadow-xl border border-border overflow-hidden max-h-[90vh] overflow-y-auto">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-border">
|
||||
<h2 className="text-lg font-semibold">Token Swap</h2>
|
||||
<h2 className="text-lg font-semibold">{t('swap.title')}</h2>
|
||||
<button onClick={onClose} className="p-2 rounded-full hover:bg-muted">
|
||||
<X className="w-5 h-5 text-muted-foreground" />
|
||||
</button>
|
||||
@@ -332,7 +334,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
{/* From Token */}
|
||||
<div className="bg-muted/50 rounded-xl p-4 space-y-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-muted-foreground">Ji (From)</span>
|
||||
<span className="text-sm text-muted-foreground">{t('swap.fromLabel')}</span>
|
||||
<select
|
||||
value={fromToken.symbol}
|
||||
onChange={(e) => {
|
||||
@@ -368,7 +370,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
Max
|
||||
</button>
|
||||
<span className="text-muted-foreground">
|
||||
Bakiye: {balances[fromToken.symbol]} {fromToken.symbol}
|
||||
{t('swap.balanceLabel')} {balances[fromToken.symbol]} {fromToken.symbol}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -386,7 +388,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
{/* To Token */}
|
||||
<div className="bg-muted/50 rounded-xl p-4 space-y-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-muted-foreground">Bo (To)</span>
|
||||
<span className="text-sm text-muted-foreground">{t('swap.toLabel')}</span>
|
||||
<select
|
||||
value={toToken.symbol}
|
||||
onChange={(e) => {
|
||||
@@ -416,7 +418,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
/>
|
||||
<div className="flex justify-end text-sm">
|
||||
<span className="text-muted-foreground">
|
||||
Bakiye: {balances[toToken.symbol]} {toToken.symbol}
|
||||
{t('swap.balanceLabel')} {balances[toToken.symbol]} {toToken.symbol}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -424,14 +426,14 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
{/* Exchange Rate */}
|
||||
<div className="bg-muted/30 rounded-xl p-3 space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Rêjeya Guherandinê</span>
|
||||
<span className="text-muted-foreground">{t('swap.exchangeRate')}</span>
|
||||
<span className="flex items-center gap-2">
|
||||
{isLoadingRate ? (
|
||||
<RefreshCw className="w-4 h-4 animate-spin" />
|
||||
) : exchangeRate ? (
|
||||
`1 ${fromToken.symbol} = ${exchangeRate.toFixed(4)} ${toToken.symbol}`
|
||||
) : (
|
||||
<span className="text-yellow-500">Pool tune</span>
|
||||
<span className="text-yellow-500">{t('swap.noPool')}</span>
|
||||
)}
|
||||
<button onClick={fetchExchangeRate} className="p-1 hover:bg-muted rounded">
|
||||
<RefreshCw className="w-3 h-3" />
|
||||
@@ -457,7 +459,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
{isSwapping ? (
|
||||
<div className="flex flex-col items-center justify-center py-4 space-y-3">
|
||||
<KurdistanSun size={80} />
|
||||
<p className="text-sm text-muted-foreground animate-pulse">Tê guhertin...</p>
|
||||
<p className="text-sm text-muted-foreground animate-pulse">{t('swap.swapping')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
@@ -465,7 +467,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) {
|
||||
disabled={!fromAmount || !exchangeRate || parseFloat(fromAmount) <= 0}
|
||||
className="w-full py-4 bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold rounded-xl disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
{!exchangeRate ? 'Pool Tune' : 'Swap Bike'}
|
||||
{!exchangeRate ? t('swap.noPoolButton') : t('swap.swapButton')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import {
|
||||
subscribeToConnection,
|
||||
getLastError,
|
||||
@@ -189,6 +190,7 @@ interface Props {
|
||||
export function TokensCard({ onSendToken }: Props) {
|
||||
const { address, balance: hezBalance } = useWallet();
|
||||
const { hapticImpact } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
const [rpcConnected, setRpcConnected] = useState(false);
|
||||
const [endpointName, setEndpointName] = useState<string | null>(null);
|
||||
|
||||
@@ -615,7 +617,7 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Token bigere..."
|
||||
placeholder={t('tokens.searchPlaceholder')}
|
||||
className="w-full pl-9 pr-4 py-2 bg-background rounded-lg text-sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -629,7 +631,7 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
className="w-full py-2 border border-dashed border-border rounded-lg text-sm text-muted-foreground hover:text-white hover:border-cyan-500/50 flex items-center justify-center gap-2"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Token Zêde Bike
|
||||
{t('tokens.addToken')}
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -639,7 +641,7 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
type="number"
|
||||
value={newAssetId}
|
||||
onChange={(e) => setNewAssetId(e.target.value)}
|
||||
placeholder="Asset ID binivîse (mînak: 3)"
|
||||
placeholder={t('tokens.assetIdPlaceholder')}
|
||||
className="w-full px-3 py-2 bg-muted rounded-lg text-sm"
|
||||
min="0"
|
||||
/>
|
||||
@@ -648,14 +650,14 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
onClick={() => setShowAddToken(false)}
|
||||
className="flex-1 py-2 bg-muted rounded-lg text-sm"
|
||||
>
|
||||
Betal
|
||||
{t('tokens.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleAddToken}
|
||||
disabled={!newAssetId}
|
||||
className="flex-1 py-2 bg-cyan-600 rounded-lg text-sm disabled:opacity-50"
|
||||
>
|
||||
Zêde Bike
|
||||
{t('tokens.add')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -667,7 +669,9 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
<div className="mx-4 mb-2 px-3 py-2 bg-green-500/10 border border-green-500/30 rounded-lg flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
|
||||
<div>
|
||||
<p className="text-xs text-green-400 font-medium">Pezkuwichain Girêdayî</p>
|
||||
<p className="text-xs text-green-400 font-medium">
|
||||
{t('tokens.blockchainConnected')}
|
||||
</p>
|
||||
{endpointName && <p className="text-[10px] text-green-400/70">{endpointName}</p>}
|
||||
</div>
|
||||
</div>
|
||||
@@ -675,9 +679,11 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
<div className="mx-4 mb-2 px-3 py-2 bg-yellow-500/10 border border-yellow-500/30 rounded-lg flex items-center gap-2">
|
||||
<RefreshCw className="w-4 h-4 text-yellow-400 animate-spin flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-xs text-yellow-400 font-medium">Girêdana Blockchain...</p>
|
||||
<p className="text-xs text-yellow-400 font-medium">
|
||||
{t('tokens.connectingBlockchain')}
|
||||
</p>
|
||||
<p className="text-[10px] text-yellow-400/70">
|
||||
{getLastError() || 'RPC serverê tê girêdan...'}
|
||||
{getLastError() || t('tokens.connectingRpc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -688,7 +694,7 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
{filteredTokens.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<Coins className="w-6 h-6 text-muted-foreground mx-auto mb-2" />
|
||||
<p className="text-sm text-muted-foreground">Token nehat dîtin</p>
|
||||
<p className="text-sm text-muted-foreground">{t('tokens.tokenNotFound')}</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredTokens.map((token) =>
|
||||
@@ -793,7 +799,7 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
{/* Total Value */}
|
||||
{token.valueUsd !== undefined && token.balance !== '--' && (
|
||||
<div className="mt-2 pt-2 border-t border-white/10 flex justify-between items-center">
|
||||
<span className="text-xs text-muted-foreground">Toplam</span>
|
||||
<span className="text-xs text-muted-foreground">{t('tokens.total')}</span>
|
||||
<span className="text-sm font-semibold">
|
||||
≈ $
|
||||
{(
|
||||
@@ -879,7 +885,9 @@ export function TokensCard({ onSendToken }: Props) {
|
||||
{token.balance}
|
||||
</p>
|
||||
{token.balance === '--' ? (
|
||||
<p className="text-xs text-muted-foreground">Tê barkirin...</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t('tokens.loadingBalance')}
|
||||
</p>
|
||||
) : token.valueUsd !== undefined ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
≈ ${token.valueUsd.toFixed(2)}
|
||||
|
||||
@@ -29,6 +29,7 @@ import { HEZStakingModal } from './HEZStakingModal';
|
||||
import { DepositUSDTModal } from './DepositUSDTModal';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { formatAddress } from '@/lib/wallet-service';
|
||||
|
||||
interface Props {
|
||||
@@ -51,6 +52,7 @@ interface Transaction {
|
||||
export function WalletDashboard({ onDisconnect }: Props) {
|
||||
const { address, balance, api, assetHubApi, disconnect, isLoading } = useWallet();
|
||||
const { hapticImpact, hapticNotification, showAlert } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<'main' | 'send' | 'receive' | 'history'>('main');
|
||||
@@ -583,7 +585,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-green-400">Girêdayî</p>
|
||||
<p className="text-xs text-green-400">{t('dashboard.connected')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -644,7 +646,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<div className="w-8 h-8 bg-green-500/20 rounded-full flex items-center justify-center">
|
||||
<Send className="w-4 h-4 text-green-400" />
|
||||
</div>
|
||||
<span className="text-xs font-medium">Bişîne</span>
|
||||
<span className="text-xs font-medium">{t('dashboard.send')}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -657,7 +659,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<div className="w-8 h-8 bg-cyan-500/20 rounded-full flex items-center justify-center">
|
||||
<QrCode className="w-4 h-4 text-cyan-400" />
|
||||
</div>
|
||||
<span className="text-xs font-medium">Werbigire</span>
|
||||
<span className="text-xs font-medium">{t('dashboard.receive')}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -702,7 +704,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<button
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
showAlert('Presale tê de ye! Zûtirîn demekê de dê bête vekirin.');
|
||||
showAlert(t('dashboard.presaleMessage'));
|
||||
}}
|
||||
className="flex flex-col items-center gap-1 p-3 bg-gradient-to-r from-pink-600/20 to-red-500/20 border border-pink-500/30 rounded-xl"
|
||||
>
|
||||
@@ -726,10 +728,8 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<img src="/tokens/USDT.png" alt="USDT" className="w-8 h-8 rounded-full" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<div className="font-semibold text-emerald-400">USDT Zêde Bike</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
TON, Polkadot an TRC20 ji zincîrên din
|
||||
</div>
|
||||
<div className="font-semibold text-emerald-400">{t('dashboard.depositUsdt')}</div>
|
||||
<div className="text-xs text-muted-foreground">{t('dashboard.depositUsdtDesc')}</div>
|
||||
</div>
|
||||
<ArrowDownLeft className="w-5 h-5 text-emerald-400" />
|
||||
</button>
|
||||
@@ -739,7 +739,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<div className="px-4 pb-4">
|
||||
<div className="bg-muted/50 border border-border rounded-xl p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold">Çalakiya Dawî</h3>
|
||||
<h3 className="font-semibold">{t('dashboard.recentActivity')}</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -747,7 +747,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
setActiveTab('history');
|
||||
}}
|
||||
className="text-gray-400 hover:text-white p-1"
|
||||
title="Dîrok"
|
||||
title={t('dashboard.history')}
|
||||
>
|
||||
<History className="w-4 h-4" />
|
||||
</button>
|
||||
@@ -755,7 +755,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
onClick={handleRefresh}
|
||||
disabled={isLoadingTxs}
|
||||
className="text-gray-400 hover:text-white p-1"
|
||||
title="Nûkirin"
|
||||
title={t('dashboard.refreshTx')}
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${isLoadingTxs ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
@@ -765,13 +765,13 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
{isLoadingTxs ? (
|
||||
<div className="text-center py-8">
|
||||
<RefreshCw className="w-8 h-8 text-gray-600 mx-auto mb-2 animate-spin" />
|
||||
<p className="text-gray-400 text-sm">Tê barkirin...</p>
|
||||
<p className="text-gray-400 text-sm">{t('dashboard.loadingTx')}</p>
|
||||
</div>
|
||||
) : recentTxs.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<History className="w-8 h-8 text-gray-600 mx-auto mb-2" />
|
||||
<p className="text-gray-500 text-sm">Transaksiyona dawî tune</p>
|
||||
<p className="text-gray-600 text-xs mt-1">Dîroka te dê li vir xuya bibe</p>
|
||||
<p className="text-gray-500 text-sm">{t('dashboard.noRecentTx')}</p>
|
||||
<p className="text-gray-600 text-xs mt-1">{t('dashboard.historyAppears')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
@@ -792,7 +792,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium">
|
||||
{tx.direction === 'sent' ? 'Şandin' : 'Wergirtin'}
|
||||
{tx.direction === 'sent' ? t('dashboard.sent') : t('dashboard.received')}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">Block #{tx.blockNumber}</div>
|
||||
</div>
|
||||
@@ -852,7 +852,9 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
{isStakingSelectorOpen && (
|
||||
<div className="fixed inset-0 z-50 bg-black/80 flex items-end justify-center">
|
||||
<div className="bg-background w-full max-w-lg rounded-t-3xl p-6 animate-in slide-in-from-bottom duration-300">
|
||||
<h2 className="text-lg font-semibold mb-4 text-center">Staking Hilbijêre</h2>
|
||||
<h2 className="text-lg font-semibold mb-4 text-center">
|
||||
{t('dashboard.selectStaking')}
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -865,8 +867,10 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<Coins className="w-6 h-6 text-green-400" />
|
||||
</div>
|
||||
<div className="font-medium">HEZ Staking</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">Validator nominate bike</div>
|
||||
<div className="text-xs text-green-400 mt-2">Trust Score +</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
{t('dashboard.validatorNominate')}
|
||||
</div>
|
||||
<div className="text-xs text-green-400 mt-2">{t('dashboard.trustScorePlus')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -880,8 +884,10 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
<Droplets className="w-6 h-6 text-purple-400" />
|
||||
</div>
|
||||
<div className="font-medium">LP Staking</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">LP token stake bike</div>
|
||||
<div className="text-xs text-purple-400 mt-2">PEZ Xelat +</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
{t('dashboard.lpStakeDesc')}
|
||||
</div>
|
||||
<div className="text-xs text-purple-400 mt-2">{t('dashboard.pezRewardPlus')}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -889,7 +895,7 @@ export function WalletDashboard({ onDisconnect }: Props) {
|
||||
onClick={() => setIsStakingSelectorOpen(false)}
|
||||
className="w-full mt-4 py-3 bg-secondary text-muted-foreground rounded-xl"
|
||||
>
|
||||
Paşve
|
||||
{t('dashboard.goBack')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -948,6 +954,7 @@ const SEND_TOKENS: TokenOption[] = [
|
||||
function SendTab({ onBack }: { onBack: () => void }) {
|
||||
const { balance, api, assetHubApi, keypair } = useWallet();
|
||||
const { hapticNotification, hapticImpact } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [selectedToken, setSelectedToken] = useState<SendToken | null>(null);
|
||||
const [toAddress, setToAddress] = useState('');
|
||||
@@ -1013,11 +1020,11 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
const tg = window.Telegram?.WebApp;
|
||||
|
||||
if (!tg?.showScanQrPopup) {
|
||||
setError('QR okuyucu vê platformê de amade nîne');
|
||||
setError(t('send.qrNotAvailable'));
|
||||
return;
|
||||
}
|
||||
|
||||
tg.showScanQrPopup({ text: 'Navnîşana cîzdanê bişoxîne' }, (scannedText: string) => {
|
||||
tg.showScanQrPopup({ text: t('send.scanQrText') }, (scannedText: string) => {
|
||||
if (scannedText) {
|
||||
// Extract address - might be raw address or URI format
|
||||
let address = scannedText;
|
||||
@@ -1035,7 +1042,7 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
tg.closeScanQrPopup?.();
|
||||
return true; // Close popup
|
||||
} else {
|
||||
setError('Navnîşana derbasdar nîne');
|
||||
setError(t('send.invalidAddress'));
|
||||
hapticNotification('error');
|
||||
}
|
||||
}
|
||||
@@ -1045,17 +1052,17 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
|
||||
const handleSend = async () => {
|
||||
if (!keypair) {
|
||||
setError('Cîzdan amade nîne');
|
||||
setError(t('send.walletNotReady'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedToken) {
|
||||
setError('Token hilbijêre');
|
||||
setError(t('send.selectTokenFirst'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toAddress || !amount) {
|
||||
setError('Navnîşan û mîqdar binivîse');
|
||||
setError(t('send.fillAddressAndAmount'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1063,20 +1070,20 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
const sendAmount = parseFloat(amount);
|
||||
|
||||
if (sendAmount > currentBalance) {
|
||||
setError('Bakiye têrê nake');
|
||||
setError(t('send.insufficientBalance'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if appropriate API is available
|
||||
if (selectedToken === 'HEZ' && !api) {
|
||||
setError('Mainnet API amade nîne');
|
||||
setError(t('send.mainnetApiNotReady'));
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(selectedToken === 'PEZ' || selectedToken === 'USDT' || selectedToken === 'DOT') &&
|
||||
!assetHubApi
|
||||
) {
|
||||
setError('Asset Hub API amade nîne');
|
||||
setError(t('send.assetHubApiNotReady'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1106,7 +1113,7 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
setSuccess(true);
|
||||
hapticNotification('success');
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Transfer neserketî');
|
||||
setError(err instanceof Error ? err.message : t('send.transferFailed'));
|
||||
hapticNotification('error');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -1119,13 +1126,13 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
<div className="p-4 space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<button onClick={onBack} className="text-muted-foreground">
|
||||
← Paş
|
||||
{t('send.back')}
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold">Token Hilbijêre</h2>
|
||||
<h2 className="text-lg font-semibold">{t('send.selectToken')}</h2>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground text-center">Kîjan token bişîne?</p>
|
||||
<p className="text-sm text-muted-foreground text-center">{t('send.whichToken')}</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
{SEND_TOKENS.map((token) => {
|
||||
@@ -1170,7 +1177,7 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
</div>
|
||||
|
||||
{(!api || !assetHubApi) && (
|
||||
<p className="text-xs text-yellow-500 text-center">⚠️ Hinek chain girêdayî nîne</p>
|
||||
<p className="text-xs text-yellow-500 text-center">{t('send.someChainNotConnected')}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -1183,9 +1190,9 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
<Check className="w-10 h-10 text-green-500" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-2">Transfer Serketî!</h2>
|
||||
<h2 className="text-xl font-semibold mb-2">{t('send.transferSuccess')}</h2>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{amount} {selectedToken} hat şandin
|
||||
{t('send.wasSent', { amount, token: selectedToken || '' })}
|
||||
</p>
|
||||
</div>
|
||||
{txHash && (
|
||||
@@ -1198,7 +1205,7 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
onClick={onBack}
|
||||
className="w-full py-3 bg-primary text-primary-foreground rounded-xl font-semibold"
|
||||
>
|
||||
Temam
|
||||
{t('send.done')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -1210,9 +1217,11 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
<div className="p-4 space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<button onClick={() => setSelectedToken(null)} className="text-muted-foreground">
|
||||
← Paş
|
||||
{t('send.back')}
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold">Bişîne {selectedToken}</h2>
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('send.sendToken', { token: selectedToken || '' })}
|
||||
</h2>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
|
||||
@@ -1231,13 +1240,13 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
onClick={() => setSelectedToken(null)}
|
||||
className="text-xs text-primary hover:underline"
|
||||
>
|
||||
Biguhere
|
||||
{t('send.changeToken')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm text-muted-foreground">Navnîşana Wergir</label>
|
||||
<label className="text-sm text-muted-foreground">{t('send.recipientAddress')}</label>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
@@ -1249,7 +1258,7 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
<button
|
||||
onClick={handleScanQR}
|
||||
className="px-4 py-3 bg-cyan-600 hover:bg-cyan-700 rounded-xl flex items-center justify-center"
|
||||
title="QR Bişoxîne"
|
||||
title={t('send.scanQr')}
|
||||
>
|
||||
<ScanLine className="w-5 h-5" />
|
||||
</button>
|
||||
@@ -1258,9 +1267,9 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between">
|
||||
<label className="text-sm text-muted-foreground">Mîqdar (HEZ)</label>
|
||||
<label className="text-sm text-muted-foreground">{t('send.amount')}</label>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Bakiye: {getCurrentBalance()} {selectedToken}
|
||||
{t('send.balanceLabel')} {getCurrentBalance()} {selectedToken}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@@ -1287,12 +1296,12 @@ function SendTab({ onBack }: { onBack: () => void }) {
|
||||
{isLoading ? (
|
||||
<>
|
||||
<RefreshCw className="w-4 h-4 animate-spin" />
|
||||
Tê şandin...
|
||||
{t('send.sending')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send className="w-4 h-4" />
|
||||
Bişîne
|
||||
{t('send.sendButton')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
@@ -1307,6 +1316,7 @@ function ReceiveTab({ address, onBack }: { address: string | null; onBack: () =>
|
||||
const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);
|
||||
const [qrError, setQrError] = useState(false);
|
||||
const { hapticNotification } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Generate QR code when address changes
|
||||
useEffect(() => {
|
||||
@@ -1344,9 +1354,9 @@ function ReceiveTab({ address, onBack }: { address: string | null; onBack: () =>
|
||||
<div className="p-4 space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<button onClick={onBack} className="text-muted-foreground">
|
||||
← Paş
|
||||
{t('receive.back')}
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold">Werbigire</h2>
|
||||
<h2 className="text-lg font-semibold">{t('receive.title')}</h2>
|
||||
<div className="w-10" />
|
||||
</div>
|
||||
|
||||
@@ -1358,16 +1368,14 @@ function ReceiveTab({ address, onBack }: { address: string | null; onBack: () =>
|
||||
) : qrError ? (
|
||||
<div className="text-center">
|
||||
<QrCode className="w-16 h-16 text-gray-400 mx-auto mb-2" />
|
||||
<p className="text-xs text-gray-500">QR çênebû</p>
|
||||
<p className="text-xs text-gray-500">{t('receive.qrFailed')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="animate-pulse bg-gray-200 w-full h-full rounded-lg" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Ev navnîşana te ye. Ji bo wergirtina token vê navnîşanê parve bike.
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">{t('receive.shareAddress')}</p>
|
||||
|
||||
<div className="p-4 bg-muted rounded-xl">
|
||||
<p className="font-mono text-sm break-all">{address}</p>
|
||||
@@ -1380,12 +1388,12 @@ function ReceiveTab({ address, onBack }: { address: string | null; onBack: () =>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check className="w-4 h-4" />
|
||||
Hat kopîkirin!
|
||||
{t('receive.addressCopied')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className="w-4 h-4" />
|
||||
Navnîşanê Kopî Bike
|
||||
{t('receive.copyAddress')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
@@ -1407,6 +1415,7 @@ function HistoryTab({
|
||||
onBack: () => void;
|
||||
}) {
|
||||
const { hapticImpact } = useTelegram();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getDecimalsForAsset = (section: string, assetId?: string): number => {
|
||||
if (section === 'balances') return 12; // HEZ
|
||||
@@ -1433,9 +1442,9 @@ function HistoryTab({
|
||||
<div className="p-4 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<button onClick={onBack} className="text-muted-foreground">
|
||||
← Paş
|
||||
{t('history.back')}
|
||||
</button>
|
||||
<h2 className="text-lg font-semibold">Dîroka Transaksiyonan</h2>
|
||||
<h2 className="text-lg font-semibold">{t('history.title')}</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
hapticImpact('light');
|
||||
@@ -1451,13 +1460,13 @@ function HistoryTab({
|
||||
{isLoading ? (
|
||||
<div className="text-center py-12">
|
||||
<RefreshCw className="w-10 h-10 text-gray-600 mx-auto mb-3 animate-spin" />
|
||||
<p className="text-gray-400">Tê barkirin...</p>
|
||||
<p className="text-gray-400">{t('history.loadingTx')}</p>
|
||||
</div>
|
||||
) : transactions.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<History className="w-12 h-12 text-gray-600 mx-auto mb-3" />
|
||||
<p className="text-gray-400 mb-1">Transaksiyona tune</p>
|
||||
<p className="text-gray-600 text-sm">Dîroka te dê li vir xuya bibe</p>
|
||||
<p className="text-gray-400 mb-1">{t('history.noTransactions')}</p>
|
||||
<p className="text-gray-600 text-sm">{t('history.historyAppears')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
@@ -1479,7 +1488,7 @@ function HistoryTab({
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium">
|
||||
{tx.direction === 'sent' ? 'Şandin' : 'Wergirtin'}
|
||||
{tx.direction === 'sent' ? t('history.sent') : t('history.received')}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">Block #{tx.blockNumber}</div>
|
||||
</div>
|
||||
@@ -1504,12 +1513,12 @@ function HistoryTab({
|
||||
<div className="pt-2 border-t border-border/50 space-y-1">
|
||||
{tx.direction === 'sent' ? (
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-gray-500">Ji bo:</span>
|
||||
<span className="text-gray-500">{t('history.to')}</span>
|
||||
<span className="font-mono text-gray-400">{formatAddress(tx.to || '')}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-gray-500">Ji:</span>
|
||||
<span className="text-gray-500">{t('history.from')}</span>
|
||||
<span className="font-mono text-gray-400">{formatAddress(tx.from)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
|
||||
import { translate } from '@/i18n';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
import {
|
||||
@@ -53,7 +54,7 @@ export function ReferralProvider({ children }: { children: ReactNode }) {
|
||||
setMyReferrals(fetchedReferrals);
|
||||
} catch (error) {
|
||||
console.error('Error fetching referral stats:', error);
|
||||
showAlert('Referral stats bar nekirin');
|
||||
showAlert(translate('context.referralStatsError'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -75,7 +76,7 @@ export function ReferralProvider({ children }: { children: ReactNode }) {
|
||||
if (event.referrer === address || event.referred === address) {
|
||||
if (event.type === 'confirmed') {
|
||||
hapticNotification('success');
|
||||
showAlert(`Referral hat pejirandin! Hejmara te: ${event.count}`);
|
||||
showAlert(translate('context.referralApproved', { count: event.count ?? 0 }));
|
||||
}
|
||||
fetchStats();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
import { validatePassword } from '@/lib/crypto';
|
||||
import { supabase } from '@/lib/supabase';
|
||||
import { useAuth } from './AuthContext';
|
||||
import { translate } from '@/i18n';
|
||||
import {
|
||||
initRPCConnection,
|
||||
subscribeToConnection,
|
||||
@@ -123,7 +124,7 @@ export function WalletProvider({ children }: { children: React.ReactNode }) {
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Wallet init error:', err);
|
||||
setError('Wallet dest pê nekir');
|
||||
setError(translate('context.walletInitFailed'));
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
@@ -138,7 +139,7 @@ export function WalletProvider({ children }: { children: React.ReactNode }) {
|
||||
setError(null);
|
||||
} else if (wasConnected && !connected) {
|
||||
// Only show disconnect error if we were previously connected
|
||||
setError('Têkiliya RPC qut bû. Dîsa girêdan tê kirin...');
|
||||
setError(translate('context.rpcDisconnected'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -195,7 +196,7 @@ export function WalletProvider({ children }: { children: React.ReactNode }) {
|
||||
async (mnemonic: string, password: string): Promise<void> => {
|
||||
// User must be authenticated first
|
||||
if (!isAuthenticated || !user?.telegram_id) {
|
||||
throw new Error('Ji kerema xwe pêşî têkeve');
|
||||
throw new Error(translate('context.pleaseLoginFirst'));
|
||||
}
|
||||
|
||||
const validation = validatePassword(password);
|
||||
@@ -221,11 +222,11 @@ export function WalletProvider({ children }: { children: React.ReactNode }) {
|
||||
async (mnemonic: string, password: string): Promise<string> => {
|
||||
// User must be authenticated first
|
||||
if (!isAuthenticated || !user?.telegram_id) {
|
||||
throw new Error('Ji kerema xwe pêşî têkeve');
|
||||
throw new Error(translate('context.pleaseLoginFirst'));
|
||||
}
|
||||
|
||||
if (!validateMnemonic(mnemonic)) {
|
||||
throw new Error('Seed phrase ne derbasdar e');
|
||||
throw new Error(translate('context.invalidSeedPhrase'));
|
||||
}
|
||||
|
||||
const validation = validatePassword(password);
|
||||
@@ -258,7 +259,7 @@ export function WalletProvider({ children }: { children: React.ReactNode }) {
|
||||
setKeypair(pair);
|
||||
setIsConnected(true);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Girêdan neserketî';
|
||||
const message = err instanceof Error ? err.message : translate('context.connectionFailed');
|
||||
setError(message);
|
||||
throw err;
|
||||
} finally {
|
||||
|
||||
@@ -17,6 +17,39 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
ar,
|
||||
};
|
||||
|
||||
// Module-level language tracking for non-React contexts
|
||||
let currentLanguage: LanguageCode = 'krd';
|
||||
|
||||
/**
|
||||
* Standalone translate function for non-React files (utils, crypto, error-tracking, etc.)
|
||||
* Language is synced from LanguageProvider automatically.
|
||||
*/
|
||||
export function translate(key: string, params?: Record<string, string | number>): string {
|
||||
let value = getNestedValue(
|
||||
translations[currentLanguage] as unknown as Record<string, unknown>,
|
||||
key
|
||||
);
|
||||
if (value === undefined && currentLanguage !== DEFAULT_LANG) {
|
||||
value = getNestedValue(translations[DEFAULT_LANG] as unknown as Record<string, unknown>, key);
|
||||
}
|
||||
if (value === undefined) return key;
|
||||
|
||||
if (params) {
|
||||
let result = value;
|
||||
for (const [paramKey, paramValue] of Object.entries(params)) {
|
||||
result = result.replace(`{${paramKey}}`, String(paramValue));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Get current language code (for locale-dependent formatting) */
|
||||
export function getCurrentLanguage(): LanguageCode {
|
||||
return currentLanguage;
|
||||
}
|
||||
|
||||
const VALID_LANGS: LanguageCode[] = ['krd', 'en', 'tr', 'ckb', 'fa', 'ar'];
|
||||
const DEFAULT_LANG: LanguageCode = 'krd';
|
||||
|
||||
@@ -96,6 +129,11 @@ export function LanguageProvider({ children }: LanguageProviderProps) {
|
||||
|
||||
const isRTL = RTL_LANGUAGES.includes(lang);
|
||||
|
||||
// Sync module-level language for standalone translate()
|
||||
useEffect(() => {
|
||||
currentLanguage = lang;
|
||||
}, [lang]);
|
||||
|
||||
// Update document direction, lang attribute, and URL when language changes
|
||||
useEffect(() => {
|
||||
document.documentElement.lang = lang === 'krd' ? 'ku' : lang;
|
||||
|
||||
@@ -299,6 +299,281 @@ const ar: Translations = {
|
||||
loadingScreen: {
|
||||
loading: 'جارٍ التحميل...',
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
connected: 'متصل',
|
||||
send: 'إرسال',
|
||||
receive: 'استلام',
|
||||
presaleMessage: 'البيع المسبق قادم! سيفتح قريباً جداً.',
|
||||
depositUsdt: 'إضافة USDT',
|
||||
depositUsdtDesc: 'من شبكات TON أو Polkadot أو TRC20',
|
||||
recentActivity: 'النشاط الأخير',
|
||||
history: 'السجل',
|
||||
refreshTx: 'تحديث',
|
||||
loadingTx: 'جاري التحميل...',
|
||||
noRecentTx: 'لا توجد معاملات حديثة',
|
||||
historyAppears: 'سيظهر سجلك هنا',
|
||||
sent: 'تم الإرسال',
|
||||
received: 'تم الاستلام',
|
||||
selectStaking: 'اختيار Staking',
|
||||
validatorNominate: 'ترشيح المدقق',
|
||||
trustScorePlus: 'Trust Score +',
|
||||
lpStakeDesc: 'رهن LP token',
|
||||
pezRewardPlus: 'مكافأة PEZ +',
|
||||
goBack: 'رجوع',
|
||||
},
|
||||
|
||||
// Send
|
||||
send: {
|
||||
back: '← رجوع',
|
||||
selectToken: 'اختيار Token',
|
||||
whichToken: 'أي token تريد إرساله؟',
|
||||
someChainNotConnected: '⚠️ بعض السلاسل غير متصلة',
|
||||
transferSuccess: 'تم التحويل بنجاح!',
|
||||
wasSent: 'تم إرسال {amount} {token}',
|
||||
done: 'تم',
|
||||
sendToken: 'إرسال {token}',
|
||||
changeToken: 'تغيير',
|
||||
recipientAddress: 'عنوان المستلم',
|
||||
scanQr: 'مسح QR',
|
||||
scanQrText: 'امسح عنوان المحفظة',
|
||||
qrNotAvailable: 'ماسح QR غير متاح على هذه المنصة',
|
||||
invalidAddress: 'عنوان غير صالح',
|
||||
amount: 'المبلغ',
|
||||
balanceLabel: 'الرصيد:',
|
||||
sending: 'جاري الإرسال...',
|
||||
sendButton: 'إرسال',
|
||||
walletNotReady: 'المحفظة غير جاهزة',
|
||||
selectTokenFirst: 'اختر token',
|
||||
fillAddressAndAmount: 'أدخل العنوان والمبلغ',
|
||||
insufficientBalance: 'رصيد غير كافٍ',
|
||||
mainnetApiNotReady: 'Mainnet API غير جاهز',
|
||||
assetHubApiNotReady: 'Asset Hub API غير جاهز',
|
||||
transferFailed: 'فشل التحويل',
|
||||
},
|
||||
|
||||
// Receive
|
||||
receive: {
|
||||
title: 'استلام',
|
||||
back: '← رجوع',
|
||||
qrFailed: 'فشل إنشاء QR',
|
||||
shareAddress: 'هذا عنوانك. شاركه لاستلام التوكن.',
|
||||
addressCopied: 'تم النسخ!',
|
||||
copyAddress: 'نسخ العنوان',
|
||||
},
|
||||
|
||||
// History
|
||||
history: {
|
||||
title: 'سجل المعاملات',
|
||||
back: '← رجوع',
|
||||
loadingTx: 'جاري التحميل...',
|
||||
noTransactions: 'لا توجد معاملات',
|
||||
historyAppears: 'سيظهر سجلك هنا',
|
||||
sent: 'تم الإرسال',
|
||||
received: 'تم الاستلام',
|
||||
to: 'إلى:',
|
||||
from: 'من:',
|
||||
},
|
||||
|
||||
// Swap
|
||||
swap: {
|
||||
title: 'Token Swap',
|
||||
fromLabel: 'من',
|
||||
toLabel: 'إلى',
|
||||
exchangeRate: 'سعر الصرف',
|
||||
noPool: 'لا يوجد Pool',
|
||||
swapping: 'جاري التبديل...',
|
||||
noPoolButton: 'لا يوجد Pool',
|
||||
swapButton: 'Swap',
|
||||
swapFailed: 'فشل Swap',
|
||||
swapSuccess: 'تم Swap بنجاح!',
|
||||
balanceLabel: 'الرصيد:',
|
||||
insufficientBalance: 'رصيد غير كافٍ',
|
||||
},
|
||||
|
||||
// Pools
|
||||
pools: {
|
||||
title: 'مجمعات السيولة',
|
||||
connectionError: 'خطأ في الاتصال - أعد المحاولة',
|
||||
loadingPools: 'جاري التحميل...',
|
||||
noPools: 'لا يوجد Pool',
|
||||
back: '← رجوع',
|
||||
addLiquidity: 'إضافة سيولة',
|
||||
removeLiquidity: 'سحب سيولة',
|
||||
yourPosition: 'موقعك',
|
||||
addButton: 'إضافة',
|
||||
removeButton: 'سحب',
|
||||
reserve: 'احتياطي',
|
||||
lpBalance: 'LP رصيد:',
|
||||
lpTokenAmount: 'مبلغ LP Token',
|
||||
amountAuto: '{token} مبلغ (تلقائي)',
|
||||
adding: 'جاري الإضافة...',
|
||||
removing: 'جاري السحب...',
|
||||
addFailed: 'فشلت الإضافة',
|
||||
removeFailed: 'فشل السحب',
|
||||
invalidLpAmount: 'مبلغ LP غير صالح',
|
||||
estimatedReturn: 'العائد المقدر:',
|
||||
success: 'نجاح!',
|
||||
addedLiquidity: 'تمت إضافة {amount0} {token0} + {amount1} {token1}',
|
||||
removedLiquidity: 'تم إرجاع {amount} LP token',
|
||||
},
|
||||
|
||||
// Staking
|
||||
staking: {
|
||||
palletNotFound: 'وحدة Staking غير موجودة',
|
||||
fetchError: 'فشل جلب معلومات staking',
|
||||
statusTab: 'الحالة',
|
||||
bondTab: 'Bond',
|
||||
nominateTab: 'ترشيح',
|
||||
unbondTab: 'Unbond',
|
||||
activeStake: 'Stake نشط',
|
||||
totalBonded: 'إجمالي Bonded',
|
||||
nominations: 'الترشيحات',
|
||||
rewardDestination: 'وجهة المكافأة',
|
||||
unbondingChunks: 'Unbonding',
|
||||
nominatedValidators: 'المدققون المرشحون',
|
||||
stakingTip: 'رهن HEZ يزيد درجة الثقة. ارهن لمدة شهر واحد على الأقل للمكافأة.',
|
||||
notStakedYet: 'لم يتم الرهن بعد',
|
||||
stakeForTrustScore: 'ارهن HEZ لكسب Trust Score',
|
||||
startStaking: 'ابدأ',
|
||||
yourBalance: 'رصيدك',
|
||||
currentlyStaked: 'مرهون حالياً',
|
||||
amountHez: 'المبلغ (HEZ)',
|
||||
bondWarning: 'الرهن يتطلب 28 يوماً للسحب.',
|
||||
bonding: 'جاري Bond...',
|
||||
bondExtra: 'إضافة المزيد',
|
||||
bondButton: 'Bond',
|
||||
bondFirst: 'أولاً bond HEZ، ثم رشّح',
|
||||
selectValidators: 'اختر مدققين (حد أقصى 16): {count}/16',
|
||||
commission: 'العمولة:',
|
||||
nominating: 'جاري الترشيح...',
|
||||
nominateButton: 'رشّح',
|
||||
notStakedUnbond: 'لم تقم بالرهن بعد',
|
||||
unbondWarning: 'Unbond يستغرق 28 يوماً. بعدها يمكنك السحب.',
|
||||
unbondProcessing: 'جاري Unbond...',
|
||||
unbondButton: 'Unbond',
|
||||
bondSuccess: 'تم رهن {amount} HEZ بنجاح!',
|
||||
nominateSuccess: 'تم ترشيح {count} مدقق بنجاح!',
|
||||
unbondSuccess: 'تم unbond {amount} HEZ! (28 يوم انتظار)',
|
||||
bondFailed: 'فشل Bond',
|
||||
nominateFailed: 'فشل الترشيح',
|
||||
unbondFailed: 'فشل Unbond',
|
||||
},
|
||||
|
||||
// LP Staking
|
||||
lpStaking: {
|
||||
palletNotReady: 'وحدة Staking غير جاهزة',
|
||||
poolsNotLoaded: 'فشل تحميل pool Staking',
|
||||
noPoolsYet: 'لا توجد pool staking بعد',
|
||||
selectPool: 'اختر Pool',
|
||||
totalStaked: 'إجمالي Staked',
|
||||
youStaked: 'أنت رهنت',
|
||||
lpBalance: 'LP رصيد',
|
||||
reward: 'مكافأة',
|
||||
stakeTab: 'Stake',
|
||||
unstakeTab: 'Unstake',
|
||||
rewardTab: 'مكافأة',
|
||||
amount: 'مبلغ ({asset})',
|
||||
balanceLabel: 'الرصيد:',
|
||||
stakedLabel: 'Staked:',
|
||||
staking: 'جاري Stake...',
|
||||
stakeButton: 'Stake',
|
||||
unstaking: 'جاري Unstake...',
|
||||
unstakeButton: 'Unstake',
|
||||
pendingRewards: 'مكافآت معلقة',
|
||||
claiming: 'جاري الاستلام...',
|
||||
claimButton: 'استلم المكافآت',
|
||||
stakeFailed: 'فشل Stake',
|
||||
stakeSuccess: 'تم Stake بنجاح!',
|
||||
unstakeFailed: 'فشل Unstake',
|
||||
unstakeSuccess: 'تم Unstake بنجاح!',
|
||||
claimFailed: 'فشل استلام المكافأة',
|
||||
claimSuccess: 'تم استلام المكافآت!',
|
||||
},
|
||||
|
||||
// Fees
|
||||
fees: {
|
||||
title: 'إضافة رسوم',
|
||||
subtitle: 'HEZ teleport',
|
||||
success: 'نجاح!',
|
||||
sentTo: 'تم إرسال {amount} HEZ إلى {chain}',
|
||||
failed: 'فشل',
|
||||
tryAgain: 'أعد المحاولة',
|
||||
targetChain: 'السلسلة المستهدفة',
|
||||
forTransfers: 'لتحويل PEZ',
|
||||
forIdentity: 'للهوية',
|
||||
minRecommended: '{description} يوصى بحد أدنى 0.1 HEZ.',
|
||||
amountHez: 'المبلغ (HEZ)',
|
||||
signing: 'وقّع المعاملة...',
|
||||
xcmTeleportPending: 'XCM Teleport قيد التنفيذ...',
|
||||
signingButton: 'جاري التوقيع...',
|
||||
processing: 'جاري المعالجة...',
|
||||
sendTo: 'إرسال إلى {chain}',
|
||||
walletNotConnected: 'المحفظة غير متصلة',
|
||||
apiNotConnected: 'API غير متصل',
|
||||
enterValidAmount: 'أدخل مبلغاً صالحاً',
|
||||
chainNotConnected: 'السلسلة غير متصلة',
|
||||
insufficientBalance: 'رصيد غير كافٍ',
|
||||
xcmPalletNotFound: 'وحدة XCM غير موجودة',
|
||||
teleportFailed: 'فشل Teleport',
|
||||
errorOccurred: 'حدث خطأ',
|
||||
},
|
||||
|
||||
// Tokens
|
||||
tokens: {
|
||||
searchPlaceholder: 'بحث token...',
|
||||
addToken: 'إضافة Token',
|
||||
assetIdPlaceholder: 'أدخل Asset ID (مثال: 3)',
|
||||
cancel: 'إلغاء',
|
||||
add: 'إضافة',
|
||||
blockchainConnected: 'Pezkuwichain متصل',
|
||||
connectingBlockchain: 'الاتصال بـ Blockchain...',
|
||||
connectingRpc: 'الاتصال بخادم RPC...',
|
||||
tokenNotFound: 'Token غير موجود',
|
||||
total: 'الإجمالي',
|
||||
loadingBalance: 'جاري التحميل...',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
networkError: 'لا يوجد اتصال بالإنترنت. يرجى التحقق من اتصالك.',
|
||||
timeout: 'استغرقت العملية وقتاً طويلاً. يرجى المحاولة مرة أخرى.',
|
||||
walletNotFound: 'المحفظة غير موجودة. يرجى إنشاء محفظة أو استعادتها.',
|
||||
wrongPassword: 'كلمة مرور خاطئة. يرجى المحاولة مرة أخرى.',
|
||||
default: 'حدث خطأ ما. يرجى المحاولة مرة أخرى.',
|
||||
},
|
||||
|
||||
// Validation
|
||||
validation: {
|
||||
minLength: 'كلمة المرور 12 حرفاً على الأقل',
|
||||
needLowercase: 'كلمة المرور تحتوي على حرف صغير واحد على الأقل (a-z)',
|
||||
needUppercase: 'كلمة المرور تحتوي على حرف كبير واحد على الأقل (A-Z)',
|
||||
needNumber: 'كلمة المرور تحتوي على رقم واحد على الأقل (0-9)',
|
||||
needSpecialChar: 'كلمة المرور تحتوي على رمز خاص واحد على الأقل (!@#$%...)',
|
||||
weakPassword: 'كلمة المرور ليست قوية بما يكفي. جرب كلمة مرور أطول بأحرف متنوعة.',
|
||||
},
|
||||
|
||||
// Time
|
||||
time: {
|
||||
now: 'الآن',
|
||||
minutesAgo: 'منذ {count} دقيقة',
|
||||
hoursAgo: 'منذ {count} ساعة',
|
||||
daysAgo: 'منذ {count} يوم',
|
||||
},
|
||||
|
||||
// Context
|
||||
context: {
|
||||
walletInitFailed: 'فشل تهيئة المحفظة',
|
||||
rpcDisconnected: 'انقطع اتصال RPC. جاري إعادة الاتصال...',
|
||||
pleaseLoginFirst: 'يرجى تسجيل الدخول أولاً',
|
||||
invalidSeedPhrase: 'Seed phrase غير صالح',
|
||||
connectionFailed: 'فشل الاتصال',
|
||||
referralStatsError: 'فشل تحميل إحصائيات الإحالة',
|
||||
referralApproved: 'تمت الموافقة على الإحالة! عددك: {count}',
|
||||
wrongPasswordError: 'كلمة مرور خاطئة',
|
||||
walletSyncFailed: 'فشل مزامنة عنوان المحفظة مع قاعدة البيانات',
|
||||
},
|
||||
};
|
||||
|
||||
export default ar;
|
||||
|
||||
@@ -301,6 +301,281 @@ const ckb: Translations = {
|
||||
loadingScreen: {
|
||||
loading: 'بارکردن...',
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
connected: 'پەیوەندی کراوە',
|
||||
send: 'ناردن',
|
||||
receive: 'وەرگرتن',
|
||||
presaleMessage: 'پرێسەیل هاتووە! زوو دەکرێتەوە.',
|
||||
depositUsdt: 'USDT زیاد بکە',
|
||||
depositUsdtDesc: 'لە TON، Polkadot یان TRC20',
|
||||
recentActivity: 'چالاکیە تازەکان',
|
||||
history: 'مێژوو',
|
||||
refreshTx: 'نوێکردنەوە',
|
||||
loadingTx: 'بارکردن...',
|
||||
noRecentTx: 'مامەڵەی تازە نییە',
|
||||
historyAppears: 'مێژووت لێرە دەردەکەوێت',
|
||||
sent: 'نێردرا',
|
||||
received: 'وەرگیرا',
|
||||
selectStaking: 'Staking هەڵبژێرە',
|
||||
validatorNominate: 'Validator ناسێنە',
|
||||
trustScorePlus: 'Trust Score +',
|
||||
lpStakeDesc: 'LP token stake بکە',
|
||||
pezRewardPlus: 'PEZ پاداشت +',
|
||||
goBack: 'گەڕانەوە',
|
||||
},
|
||||
|
||||
// Send
|
||||
send: {
|
||||
back: '← گەڕانەوە',
|
||||
selectToken: 'Token هەڵبژێرە',
|
||||
whichToken: 'کام token بنێرە؟',
|
||||
someChainNotConnected: '⚠️ هەندێ chain پەیوەندی نییە',
|
||||
transferSuccess: 'ناردن سەرکەوتوو بوو!',
|
||||
wasSent: '{amount} {token} نێردرا',
|
||||
done: 'تەواو',
|
||||
sendToken: 'بنێرە {token}',
|
||||
changeToken: 'بیگۆرە',
|
||||
recipientAddress: 'ناونیشانی وەرگر',
|
||||
scanQr: 'QR بخوێنەوە',
|
||||
scanQrText: 'ناونیشانی جزدان بخوێنەوە',
|
||||
qrNotAvailable: 'QR خوێنەرەوە لەم سیستەمەدا ئامادە نییە',
|
||||
invalidAddress: 'ناونیشانی نادروست',
|
||||
amount: 'بڕ',
|
||||
balanceLabel: 'باڵانس:',
|
||||
sending: 'دەنێردرێت...',
|
||||
sendButton: 'بنێرە',
|
||||
walletNotReady: 'جزدان ئامادە نییە',
|
||||
selectTokenFirst: 'Token هەڵبژێرە',
|
||||
fillAddressAndAmount: 'ناونیشان و بڕ بنووسە',
|
||||
insufficientBalance: 'باڵانس بەسنییە',
|
||||
mainnetApiNotReady: 'Mainnet API ئامادە نییە',
|
||||
assetHubApiNotReady: 'Asset Hub API ئامادە نییە',
|
||||
transferFailed: 'ناردن سەرنەکەوت',
|
||||
},
|
||||
|
||||
// Receive
|
||||
receive: {
|
||||
title: 'وەرگرتن',
|
||||
back: '← گەڕانەوە',
|
||||
qrFailed: 'QR دروست نەبوو',
|
||||
shareAddress: 'ئەمە ناونیشانتە. بۆ وەرگرتنی token ئەم ناونیشانە بڵاو بکەوە.',
|
||||
addressCopied: 'کۆپی کرا!',
|
||||
copyAddress: 'ناونیشان کۆپی بکە',
|
||||
},
|
||||
|
||||
// History
|
||||
history: {
|
||||
title: 'مێژووی مامەڵەکان',
|
||||
back: '← گەڕانەوە',
|
||||
loadingTx: 'بارکردن...',
|
||||
noTransactions: 'مامەڵە نییە',
|
||||
historyAppears: 'مێژووت لێرە دەردەکەوێت',
|
||||
sent: 'نێردرا',
|
||||
received: 'وەرگیرا',
|
||||
to: 'بۆ:',
|
||||
from: 'لە:',
|
||||
},
|
||||
|
||||
// Swap
|
||||
swap: {
|
||||
title: 'Token Swap',
|
||||
fromLabel: 'لە',
|
||||
toLabel: 'بۆ',
|
||||
exchangeRate: 'ڕێژەی گۆڕین',
|
||||
noPool: 'Pool نییە',
|
||||
swapping: 'دەگۆڕدرێت...',
|
||||
noPoolButton: 'Pool نییە',
|
||||
swapButton: 'Swap بکە',
|
||||
swapFailed: 'Swap سەرنەکەوت',
|
||||
swapSuccess: 'Swap سەرکەوتوو بوو!',
|
||||
balanceLabel: 'باڵانس:',
|
||||
insufficientBalance: 'باڵانس بەسنییە',
|
||||
},
|
||||
|
||||
// Pools
|
||||
pools: {
|
||||
title: 'Liquidity Pools',
|
||||
connectionError: 'هەڵەی پەیوەندی - دوبارە هەوڵ بدەوە',
|
||||
loadingPools: 'بارکردن...',
|
||||
noPools: 'Pool نییە',
|
||||
back: '← گەڕانەوە',
|
||||
addLiquidity: 'Liquidity زیاد بکە',
|
||||
removeLiquidity: 'Liquidity دەربهێنە',
|
||||
yourPosition: 'پۆزیشنی تۆ',
|
||||
addButton: 'زیاد بکە',
|
||||
removeButton: 'دەربهێنە',
|
||||
reserve: 'ئەندۆخته',
|
||||
lpBalance: 'LP باڵانس:',
|
||||
lpTokenAmount: 'LP Token بڕ',
|
||||
amountAuto: '{token} بڕ (خۆکار)',
|
||||
adding: 'زیاد دەکرێت...',
|
||||
removing: 'دەردەهێنرێت...',
|
||||
addFailed: 'زیادکردن سەرنەکەوت',
|
||||
removeFailed: 'دەرهێنان سەرنەکەوت',
|
||||
invalidLpAmount: 'بڕی LP نادروستە',
|
||||
estimatedReturn: 'تەخمینی گەڕانەوە:',
|
||||
success: 'سەرکەوتوو!',
|
||||
addedLiquidity: '{amount0} {token0} + {amount1} {token1} زیاد کرا',
|
||||
removedLiquidity: '{amount} LP token گەڕێنرایەوە',
|
||||
},
|
||||
|
||||
// Staking
|
||||
staking: {
|
||||
palletNotFound: 'Staking pallet نەدۆزرایەوە',
|
||||
fetchError: 'زانیاریی staking نەهاتە دەست',
|
||||
statusTab: 'بارودۆخ',
|
||||
bondTab: 'Bond',
|
||||
nominateTab: 'Nominate',
|
||||
unbondTab: 'Unbond',
|
||||
activeStake: 'Stake چالاک',
|
||||
totalBonded: 'کۆی Bonded',
|
||||
nominations: 'ناساندنەکان',
|
||||
rewardDestination: 'مەبەستی پاداشت',
|
||||
unbondingChunks: 'Unbonding',
|
||||
nominatedValidators: 'Validator ناسێنراوەکان',
|
||||
stakingTip: 'HEZ stake کردن Trust Score زیاد دەکات. لانیکەم ١ مانگ stake بکە بۆ بۆنەس.',
|
||||
notStakedYet: 'هێشتا Stake نەکراوە',
|
||||
stakeForTrustScore: 'HEZ stake بکە بۆ بەدەستهێنانی Trust Score',
|
||||
startStaking: 'دەست پێ بکە',
|
||||
yourBalance: 'باڵانسی تۆ',
|
||||
currentlyStaked: 'ئێستا Staked',
|
||||
amountHez: 'بڕ (HEZ)',
|
||||
bondWarning: 'Stake کردن ٢٨ ڕۆژ چاوەڕوانییە بۆ دەرهێنان.',
|
||||
bonding: 'Bond دەکرێت...',
|
||||
bondExtra: 'زیاد بکە',
|
||||
bondButton: 'Bond بکە',
|
||||
bondFirst: 'سەرەتا HEZ bond بکە، دواتر nominate بکە',
|
||||
selectValidators: 'Validator هەڵبژێرە (زۆرینە ١٦): {count}/16',
|
||||
commission: 'کۆمیسیۆن:',
|
||||
nominating: 'Nominate دەکرێت...',
|
||||
nominateButton: 'Nominate بکە',
|
||||
notStakedUnbond: 'هێشتا stake نەکراوە',
|
||||
unbondWarning: 'Unbond کردن ٢٨ ڕۆژ دەخایەنێت. دواتر دەتوانیت دەربهێنیت.',
|
||||
unbondProcessing: 'Unbond دەکرێت...',
|
||||
unbondButton: 'Unbond بکە',
|
||||
bondSuccess: '{amount} HEZ بە سەرکەوتوویی stake کرا!',
|
||||
nominateSuccess: '{count} validator بە سەرکەوتوویی nominate کران!',
|
||||
unbondSuccess: '{amount} HEZ unbond کرا! (٢٨ ڕۆژ چاوەڕوانی)',
|
||||
bondFailed: 'Bond سەرنەکەوت',
|
||||
nominateFailed: 'Nominate سەرنەکەوت',
|
||||
unbondFailed: 'Unbond سەرنەکەوت',
|
||||
},
|
||||
|
||||
// LP Staking
|
||||
lpStaking: {
|
||||
palletNotReady: 'Staking pallet ئامادە نییە',
|
||||
poolsNotLoaded: 'Staking pools بار نەکران',
|
||||
noPoolsYet: 'هێشتا staking pool نییە',
|
||||
selectPool: 'Pool هەڵبژێرە',
|
||||
totalStaked: 'کۆی Staked',
|
||||
youStaked: 'تۆ Stake کردووە',
|
||||
lpBalance: 'LP باڵانس',
|
||||
reward: 'پاداشت',
|
||||
stakeTab: 'Stake',
|
||||
unstakeTab: 'Unstake',
|
||||
rewardTab: 'پاداشت',
|
||||
amount: 'بڕ ({asset})',
|
||||
balanceLabel: 'باڵانس:',
|
||||
stakedLabel: 'Staked:',
|
||||
staking: 'Stake دەکرێت...',
|
||||
stakeButton: 'Stake بکە',
|
||||
unstaking: 'Unstake دەکرێت...',
|
||||
unstakeButton: 'Unstake بکە',
|
||||
pendingRewards: 'پاداشتە چاوەڕوانەکان',
|
||||
claiming: 'وەردەگیرێت...',
|
||||
claimButton: 'پاداشتەکان وەربگرە',
|
||||
stakeFailed: 'Stake سەرنەکەوت',
|
||||
stakeSuccess: 'Stake سەرکەوتوو بوو!',
|
||||
unstakeFailed: 'Unstake سەرنەکەوت',
|
||||
unstakeSuccess: 'Unstake سەرکەوتوو بوو!',
|
||||
claimFailed: 'پاداشت وەرنەگیرا',
|
||||
claimSuccess: 'پاداشت وەرگیرا!',
|
||||
},
|
||||
|
||||
// Fees
|
||||
fees: {
|
||||
title: 'Fee زیاد بکە',
|
||||
subtitle: 'HEZ teleport',
|
||||
success: 'سەرکەوتوو!',
|
||||
sentTo: '{amount} HEZ بۆ {chain} نێردرا',
|
||||
failed: 'سەرنەکەوت',
|
||||
tryAgain: 'دوبارە هەوڵ بدەوە',
|
||||
targetChain: 'زنجیرەی ئامانج',
|
||||
forTransfers: 'بۆ ناردنی PEZ',
|
||||
forIdentity: 'بۆ ناسنامە',
|
||||
minRecommended: '{description} لانیکەم ٠.١ HEZ پێشنیار دەکرێت.',
|
||||
amountHez: 'بڕ (HEZ)',
|
||||
signing: 'مامەڵە واژوو بکە...',
|
||||
xcmTeleportPending: 'XCM Teleport بەردەوامە...',
|
||||
signingButton: 'واژوو دەکرێت...',
|
||||
processing: 'کاردەکرێت...',
|
||||
sendTo: 'بۆ {chain} بنێرە',
|
||||
walletNotConnected: 'جزدان پەیوەندی نییە',
|
||||
apiNotConnected: 'API پەیوەندی نییە',
|
||||
enterValidAmount: 'بڕێکی دروست بنووسە',
|
||||
chainNotConnected: 'زنجیرە پەیوەندی نییە',
|
||||
insufficientBalance: 'باڵانس بەسنییە',
|
||||
xcmPalletNotFound: 'XCM pallet نەدۆزرایەوە',
|
||||
teleportFailed: 'Teleport سەرنەکەوت',
|
||||
errorOccurred: 'هەڵەیەک ڕوویدا',
|
||||
},
|
||||
|
||||
// Tokens
|
||||
tokens: {
|
||||
searchPlaceholder: 'Token بگەڕێ...',
|
||||
addToken: 'Token زیاد بکە',
|
||||
assetIdPlaceholder: 'Asset ID بنووسە (نموونە: 3)',
|
||||
cancel: 'هەڵوەشاندنەوە',
|
||||
add: 'زیاد بکە',
|
||||
blockchainConnected: 'Pezkuwichain پەیوەندی کراوە',
|
||||
connectingBlockchain: 'پەیوەندی بە Blockchain...',
|
||||
connectingRpc: 'پەیوەندی بە RPC...',
|
||||
tokenNotFound: 'Token نەدۆزرایەوە',
|
||||
total: 'کۆی گشتی',
|
||||
loadingBalance: 'بارکردن...',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
networkError: 'پەیوەندی ئینتەرنێت نییە. تکایە پەیوەندیت بپشکنە.',
|
||||
timeout: 'کارەکە زۆر درێژ کێشا. تکایە دوبارە هەوڵ بدەوە.',
|
||||
walletNotFound: 'جزدان نەدۆزرایەوە. تکایە جزدان دروست بکە یان گەڕاندنەوە بکە.',
|
||||
wrongPassword: 'وشەی نهێنی هەڵەیە. تکایە دوبارە هەوڵ بدەوە.',
|
||||
default: 'شتێک هەڵەی کرد. تکایە دوبارە هەوڵ بدەوە.',
|
||||
},
|
||||
|
||||
// Validation
|
||||
validation: {
|
||||
minLength: 'وشەی نهێنی لانیکەم ١٢ پیت بێت',
|
||||
needLowercase: 'وشەی نهێنی لانیکەم ١ پیتی بچووک هەبێت (a-z)',
|
||||
needUppercase: 'وشەی نهێنی لانیکەم ١ پیتی گەورە هەبێت (A-Z)',
|
||||
needNumber: 'وشەی نهێنی لانیکەم ١ ژمارە هەبێت (0-9)',
|
||||
needSpecialChar: 'وشەی نهێنی لانیکەم ١ هێمای تایبەت هەبێت (!@#$%...)',
|
||||
weakPassword: 'وشەی نهێنی بەس بەهێز نییە. وشەیەکی نهێنی درێژتر بە پیتی جۆراوجۆر تاقی بکەوە.',
|
||||
},
|
||||
|
||||
// Time
|
||||
time: {
|
||||
now: 'ئێستا',
|
||||
minutesAgo: '{count} خولەک پێش',
|
||||
hoursAgo: '{count} کاتژمێر پێش',
|
||||
daysAgo: '{count} ڕۆژ پێش',
|
||||
},
|
||||
|
||||
// Context
|
||||
context: {
|
||||
walletInitFailed: 'جزدان دەست پێ نەکرد',
|
||||
rpcDisconnected: 'پەیوەندیی RPC پچڕا. دوبارە پەیوەندی دەکرێت...',
|
||||
pleaseLoginFirst: 'تکایە سەرەتا بچۆ ژوورەوە',
|
||||
invalidSeedPhrase: 'Seed phrase نادروستە',
|
||||
connectionFailed: 'پەیوەندی سەرنەکەوت',
|
||||
referralStatsError: 'ئامارەکانی referral بار نەکران',
|
||||
referralApproved: 'Referral پەسەند کرا! ژمارەت: {count}',
|
||||
wrongPasswordError: 'وشەی نهێنی هەڵەیە',
|
||||
walletSyncFailed: 'هاوکاتکردنی ناونیشانی جزدان لەگەڵ DB سەرنەکەوت',
|
||||
},
|
||||
};
|
||||
|
||||
export default ckb;
|
||||
|
||||
@@ -300,6 +300,281 @@ const en: Translations = {
|
||||
loadingScreen: {
|
||||
loading: 'Loading...',
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
connected: 'Connected',
|
||||
send: 'Send',
|
||||
receive: 'Receive',
|
||||
presaleMessage: 'Presale is coming! It will open very soon.',
|
||||
depositUsdt: 'Add USDT',
|
||||
depositUsdtDesc: 'From TON, Polkadot or TRC20 networks',
|
||||
recentActivity: 'Recent Activity',
|
||||
history: 'History',
|
||||
refreshTx: 'Refresh',
|
||||
loadingTx: 'Loading...',
|
||||
noRecentTx: 'No recent transactions',
|
||||
historyAppears: 'Your history will appear here',
|
||||
sent: 'Sent',
|
||||
received: 'Received',
|
||||
selectStaking: 'Select Staking',
|
||||
validatorNominate: 'Nominate validators',
|
||||
trustScorePlus: 'Trust Score +',
|
||||
lpStakeDesc: 'Stake LP tokens',
|
||||
pezRewardPlus: 'PEZ Reward +',
|
||||
goBack: 'Back',
|
||||
},
|
||||
|
||||
// Send
|
||||
send: {
|
||||
back: '← Back',
|
||||
selectToken: 'Select Token',
|
||||
whichToken: 'Which token to send?',
|
||||
someChainNotConnected: '⚠️ Some chains are not connected',
|
||||
transferSuccess: 'Transfer Successful!',
|
||||
wasSent: '{amount} {token} was sent',
|
||||
done: 'Done',
|
||||
sendToken: 'Send {token}',
|
||||
changeToken: 'Change',
|
||||
recipientAddress: 'Recipient Address',
|
||||
scanQr: 'Scan QR',
|
||||
scanQrText: 'Scan wallet address',
|
||||
qrNotAvailable: 'QR scanner is not available on this platform',
|
||||
invalidAddress: 'Invalid address',
|
||||
amount: 'Amount',
|
||||
balanceLabel: 'Balance:',
|
||||
sending: 'Sending...',
|
||||
sendButton: 'Send',
|
||||
walletNotReady: 'Wallet is not ready',
|
||||
selectTokenFirst: 'Select a token',
|
||||
fillAddressAndAmount: 'Enter address and amount',
|
||||
insufficientBalance: 'Insufficient balance',
|
||||
mainnetApiNotReady: 'Mainnet API is not ready',
|
||||
assetHubApiNotReady: 'Asset Hub API is not ready',
|
||||
transferFailed: 'Transfer failed',
|
||||
},
|
||||
|
||||
// Receive
|
||||
receive: {
|
||||
title: 'Receive',
|
||||
back: '← Back',
|
||||
qrFailed: 'QR generation failed',
|
||||
shareAddress: 'This is your address. Share it to receive tokens.',
|
||||
addressCopied: 'Copied!',
|
||||
copyAddress: 'Copy Address',
|
||||
},
|
||||
|
||||
// History
|
||||
history: {
|
||||
title: 'Transaction History',
|
||||
back: '← Back',
|
||||
loadingTx: 'Loading...',
|
||||
noTransactions: 'No transactions',
|
||||
historyAppears: 'Your history will appear here',
|
||||
sent: 'Sent',
|
||||
received: 'Received',
|
||||
to: 'To:',
|
||||
from: 'From:',
|
||||
},
|
||||
|
||||
// Swap
|
||||
swap: {
|
||||
title: 'Token Swap',
|
||||
fromLabel: 'From',
|
||||
toLabel: 'To',
|
||||
exchangeRate: 'Exchange Rate',
|
||||
noPool: 'No pool',
|
||||
swapping: 'Swapping...',
|
||||
noPoolButton: 'No Pool',
|
||||
swapButton: 'Swap',
|
||||
swapFailed: 'Swap failed',
|
||||
swapSuccess: 'Swap Successful!',
|
||||
balanceLabel: 'Balance:',
|
||||
insufficientBalance: 'Insufficient balance',
|
||||
},
|
||||
|
||||
// Pools
|
||||
pools: {
|
||||
title: 'Liquidity Pools',
|
||||
connectionError: 'Connection error - please retry',
|
||||
loadingPools: 'Loading...',
|
||||
noPools: 'No pools',
|
||||
back: '← Back',
|
||||
addLiquidity: 'Add Liquidity',
|
||||
removeLiquidity: 'Remove Liquidity',
|
||||
yourPosition: 'Your Position',
|
||||
addButton: 'Add',
|
||||
removeButton: 'Remove',
|
||||
reserve: 'Reserve',
|
||||
lpBalance: 'LP Balance:',
|
||||
lpTokenAmount: 'LP Token Amount',
|
||||
amountAuto: '{token} Amount (auto)',
|
||||
adding: 'Adding...',
|
||||
removing: 'Removing...',
|
||||
addFailed: 'Add liquidity failed',
|
||||
removeFailed: 'Remove liquidity failed',
|
||||
invalidLpAmount: 'Invalid LP amount',
|
||||
estimatedReturn: 'Estimated return:',
|
||||
success: 'Success!',
|
||||
addedLiquidity: '{amount0} {token0} + {amount1} {token1} added',
|
||||
removedLiquidity: '{amount} LP tokens returned',
|
||||
},
|
||||
|
||||
// Staking
|
||||
staking: {
|
||||
palletNotFound: 'Staking pallet not found',
|
||||
fetchError: 'Failed to fetch staking info',
|
||||
statusTab: 'Status',
|
||||
bondTab: 'Bond',
|
||||
nominateTab: 'Nominate',
|
||||
unbondTab: 'Unbond',
|
||||
activeStake: 'Active Stake',
|
||||
totalBonded: 'Total Bonded',
|
||||
nominations: 'Nominations',
|
||||
rewardDestination: 'Reward Destination',
|
||||
unbondingChunks: 'Unbonding',
|
||||
nominatedValidators: 'Nominated Validators',
|
||||
stakingTip: 'Staking HEZ increases Trust Score. Stake for at least 1 month for bonus.',
|
||||
notStakedYet: 'Not Staked Yet',
|
||||
stakeForTrustScore: 'Stake HEZ to earn Trust Score',
|
||||
startStaking: 'Start Staking',
|
||||
yourBalance: 'Your Balance',
|
||||
currentlyStaked: 'Currently Staked',
|
||||
amountHez: 'Amount (HEZ)',
|
||||
bondWarning: 'Staking requires 28 days for withdrawal.',
|
||||
bonding: 'Bonding...',
|
||||
bondExtra: 'Add More',
|
||||
bondButton: 'Bond',
|
||||
bondFirst: 'First bond HEZ, then nominate',
|
||||
selectValidators: 'Select validators (max 16): {count}/16',
|
||||
commission: 'Commission:',
|
||||
nominating: 'Nominating...',
|
||||
nominateButton: 'Nominate',
|
||||
notStakedUnbond: 'You have not staked yet',
|
||||
unbondWarning: 'Unbonding takes 28 days. Then you can withdraw.',
|
||||
unbondProcessing: 'Unbonding...',
|
||||
unbondButton: 'Unbond',
|
||||
bondSuccess: '{amount} HEZ staked successfully!',
|
||||
nominateSuccess: '{count} validators nominated successfully!',
|
||||
unbondSuccess: '{amount} HEZ unbonded! (28 days waiting)',
|
||||
bondFailed: 'Bond failed',
|
||||
nominateFailed: 'Nominate failed',
|
||||
unbondFailed: 'Unbond failed',
|
||||
},
|
||||
|
||||
// LP Staking
|
||||
lpStaking: {
|
||||
palletNotReady: 'Staking pallet is not ready',
|
||||
poolsNotLoaded: 'Failed to load staking pools',
|
||||
noPoolsYet: 'No staking pools yet',
|
||||
selectPool: 'Select Pool',
|
||||
totalStaked: 'Total Staked',
|
||||
youStaked: 'You Staked',
|
||||
lpBalance: 'LP Balance',
|
||||
reward: 'Reward',
|
||||
stakeTab: 'Stake',
|
||||
unstakeTab: 'Unstake',
|
||||
rewardTab: 'Rewards',
|
||||
amount: 'Amount ({asset})',
|
||||
balanceLabel: 'Balance:',
|
||||
stakedLabel: 'Staked:',
|
||||
staking: 'Staking...',
|
||||
stakeButton: 'Stake',
|
||||
unstaking: 'Unstaking...',
|
||||
unstakeButton: 'Unstake',
|
||||
pendingRewards: 'Pending Rewards',
|
||||
claiming: 'Claiming...',
|
||||
claimButton: 'Claim Rewards',
|
||||
stakeFailed: 'Stake failed',
|
||||
stakeSuccess: 'Staked successfully!',
|
||||
unstakeFailed: 'Unstake failed',
|
||||
unstakeSuccess: 'Unstaked successfully!',
|
||||
claimFailed: 'Claim failed',
|
||||
claimSuccess: 'Rewards claimed!',
|
||||
},
|
||||
|
||||
// Fees
|
||||
fees: {
|
||||
title: 'Add Fee',
|
||||
subtitle: 'HEZ teleport',
|
||||
success: 'Success!',
|
||||
sentTo: '{amount} HEZ sent to {chain}',
|
||||
failed: 'Failed',
|
||||
tryAgain: 'Try Again',
|
||||
targetChain: 'Target Chain',
|
||||
forTransfers: 'For PEZ transfers',
|
||||
forIdentity: 'For identity',
|
||||
minRecommended: '{description} minimum 0.1 HEZ recommended.',
|
||||
amountHez: 'Amount (HEZ)',
|
||||
signing: 'Sign the transaction...',
|
||||
xcmTeleportPending: 'XCM Teleport in progress...',
|
||||
signingButton: 'Signing...',
|
||||
processing: 'Processing...',
|
||||
sendTo: 'Send to {chain}',
|
||||
walletNotConnected: 'Wallet not connected',
|
||||
apiNotConnected: 'API not connected',
|
||||
enterValidAmount: 'Enter a valid amount',
|
||||
chainNotConnected: 'Chain not connected',
|
||||
insufficientBalance: 'Insufficient balance',
|
||||
xcmPalletNotFound: 'XCM pallet not found',
|
||||
teleportFailed: 'Teleport failed',
|
||||
errorOccurred: 'An error occurred',
|
||||
},
|
||||
|
||||
// Tokens
|
||||
tokens: {
|
||||
searchPlaceholder: 'Search token...',
|
||||
addToken: 'Add Token',
|
||||
assetIdPlaceholder: 'Enter Asset ID (e.g. 3)',
|
||||
cancel: 'Cancel',
|
||||
add: 'Add',
|
||||
blockchainConnected: 'Pezkuwichain Connected',
|
||||
connectingBlockchain: 'Connecting to Blockchain...',
|
||||
connectingRpc: 'Connecting to RPC server...',
|
||||
tokenNotFound: 'Token not found',
|
||||
total: 'Total',
|
||||
loadingBalance: 'Loading...',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
networkError: 'No internet connection. Please check your connection.',
|
||||
timeout: 'Operation took too long. Please try again.',
|
||||
walletNotFound: 'Wallet not found. Please create or restore a wallet.',
|
||||
wrongPassword: 'Wrong password. Please try again.',
|
||||
default: 'Something went wrong. Please try again.',
|
||||
},
|
||||
|
||||
// Validation
|
||||
validation: {
|
||||
minLength: 'Password must be at least 12 characters',
|
||||
needLowercase: 'Password must contain at least 1 lowercase letter (a-z)',
|
||||
needUppercase: 'Password must contain at least 1 uppercase letter (A-Z)',
|
||||
needNumber: 'Password must contain at least 1 number (0-9)',
|
||||
needSpecialChar: 'Password must contain at least 1 special character (!@#$%...)',
|
||||
weakPassword: 'Password is not strong enough. Try a longer password with diverse characters.',
|
||||
},
|
||||
|
||||
// Time
|
||||
time: {
|
||||
now: 'Now',
|
||||
minutesAgo: '{count} min ago',
|
||||
hoursAgo: '{count} hours ago',
|
||||
daysAgo: '{count} days ago',
|
||||
},
|
||||
|
||||
// Context
|
||||
context: {
|
||||
walletInitFailed: 'Wallet initialization failed',
|
||||
rpcDisconnected: 'RPC connection lost. Reconnecting...',
|
||||
pleaseLoginFirst: 'Please log in first',
|
||||
invalidSeedPhrase: 'Invalid seed phrase',
|
||||
connectionFailed: 'Connection failed',
|
||||
referralStatsError: 'Failed to load referral stats',
|
||||
referralApproved: 'Referral approved! Your count: {count}',
|
||||
wrongPasswordError: 'Wrong password',
|
||||
walletSyncFailed: 'Wallet address sync to DB failed',
|
||||
},
|
||||
};
|
||||
|
||||
export default en;
|
||||
|
||||
@@ -300,6 +300,281 @@ const fa: Translations = {
|
||||
loadingScreen: {
|
||||
loading: 'در حال بارگذاری...',
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
connected: 'متصل',
|
||||
send: 'ارسال',
|
||||
receive: 'دریافت',
|
||||
presaleMessage: 'پیشفروش در راه است! به زودی باز میشود.',
|
||||
depositUsdt: 'افزودن USDT',
|
||||
depositUsdtDesc: 'از شبکههای TON، Polkadot یا TRC20',
|
||||
recentActivity: 'فعالیتهای اخیر',
|
||||
history: 'تاریخچه',
|
||||
refreshTx: 'بروزرسانی',
|
||||
loadingTx: 'در حال بارگذاری...',
|
||||
noRecentTx: 'تراکنش اخیری نیست',
|
||||
historyAppears: 'تاریخچه شما اینجا نمایش داده میشود',
|
||||
sent: 'ارسال شد',
|
||||
received: 'دریافت شد',
|
||||
selectStaking: 'انتخاب Staking',
|
||||
validatorNominate: 'نامزد کردن اعتبارسنج',
|
||||
trustScorePlus: 'Trust Score +',
|
||||
lpStakeDesc: 'استیک کردن LP token',
|
||||
pezRewardPlus: 'پاداش PEZ +',
|
||||
goBack: 'بازگشت',
|
||||
},
|
||||
|
||||
// Send
|
||||
send: {
|
||||
back: '← بازگشت',
|
||||
selectToken: 'انتخاب Token',
|
||||
whichToken: 'کدام token را ارسال کنید؟',
|
||||
someChainNotConnected: '⚠️ برخی زنجیرهها متصل نیستند',
|
||||
transferSuccess: 'انتقال موفق!',
|
||||
wasSent: '{amount} {token} ارسال شد',
|
||||
done: 'تمام',
|
||||
sendToken: 'ارسال {token}',
|
||||
changeToken: 'تغییر',
|
||||
recipientAddress: 'آدرس گیرنده',
|
||||
scanQr: 'اسکن QR',
|
||||
scanQrText: 'آدرس کیف پول را اسکن کنید',
|
||||
qrNotAvailable: 'اسکنر QR در این پلتفرم موجود نیست',
|
||||
invalidAddress: 'آدرس نامعتبر',
|
||||
amount: 'مقدار',
|
||||
balanceLabel: 'موجودی:',
|
||||
sending: 'در حال ارسال...',
|
||||
sendButton: 'ارسال',
|
||||
walletNotReady: 'کیف پول آماده نیست',
|
||||
selectTokenFirst: 'Token انتخاب کنید',
|
||||
fillAddressAndAmount: 'آدرس و مقدار را وارد کنید',
|
||||
insufficientBalance: 'موجودی کافی نیست',
|
||||
mainnetApiNotReady: 'Mainnet API آماده نیست',
|
||||
assetHubApiNotReady: 'Asset Hub API آماده نیست',
|
||||
transferFailed: 'انتقال ناموفق',
|
||||
},
|
||||
|
||||
// Receive
|
||||
receive: {
|
||||
title: 'دریافت',
|
||||
back: '← بازگشت',
|
||||
qrFailed: 'ساخت QR ناموفق',
|
||||
shareAddress: 'این آدرس شماست. برای دریافت token این آدرس را به اشتراک بگذارید.',
|
||||
addressCopied: 'کپی شد!',
|
||||
copyAddress: 'کپی آدرس',
|
||||
},
|
||||
|
||||
// History
|
||||
history: {
|
||||
title: 'تاریخچه تراکنشها',
|
||||
back: '← بازگشت',
|
||||
loadingTx: 'در حال بارگذاری...',
|
||||
noTransactions: 'تراکنشی نیست',
|
||||
historyAppears: 'تاریخچه شما اینجا نمایش داده میشود',
|
||||
sent: 'ارسال شد',
|
||||
received: 'دریافت شد',
|
||||
to: 'به:',
|
||||
from: 'از:',
|
||||
},
|
||||
|
||||
// Swap
|
||||
swap: {
|
||||
title: 'Token Swap',
|
||||
fromLabel: 'از',
|
||||
toLabel: 'به',
|
||||
exchangeRate: 'نرخ تبدیل',
|
||||
noPool: 'Pool نیست',
|
||||
swapping: 'در حال تبدیل...',
|
||||
noPoolButton: 'Pool نیست',
|
||||
swapButton: 'Swap',
|
||||
swapFailed: 'Swap ناموفق',
|
||||
swapSuccess: 'Swap موفق!',
|
||||
balanceLabel: 'موجودی:',
|
||||
insufficientBalance: 'موجودی کافی نیست',
|
||||
},
|
||||
|
||||
// Pools
|
||||
pools: {
|
||||
title: 'Liquidity Pools',
|
||||
connectionError: 'خطای اتصال - دوباره تلاش کنید',
|
||||
loadingPools: 'در حال بارگذاری...',
|
||||
noPools: 'Pool نیست',
|
||||
back: '← بازگشت',
|
||||
addLiquidity: 'افزودن Liquidity',
|
||||
removeLiquidity: 'برداشت Liquidity',
|
||||
yourPosition: 'موقعیت شما',
|
||||
addButton: 'افزودن',
|
||||
removeButton: 'برداشت',
|
||||
reserve: 'ذخیره',
|
||||
lpBalance: 'LP موجودی:',
|
||||
lpTokenAmount: 'مقدار LP Token',
|
||||
amountAuto: '{token} مقدار (خودکار)',
|
||||
adding: 'در حال افزودن...',
|
||||
removing: 'در حال برداشت...',
|
||||
addFailed: 'افزودن ناموفق',
|
||||
removeFailed: 'برداشت ناموفق',
|
||||
invalidLpAmount: 'مقدار LP نامعتبر',
|
||||
estimatedReturn: 'بازگشت تخمینی:',
|
||||
success: 'موفق!',
|
||||
addedLiquidity: '{amount0} {token0} + {amount1} {token1} اضافه شد',
|
||||
removedLiquidity: '{amount} LP token بازگردانده شد',
|
||||
},
|
||||
|
||||
// Staking
|
||||
staking: {
|
||||
palletNotFound: 'ماژول Staking یافت نشد',
|
||||
fetchError: 'اطلاعات staking دریافت نشد',
|
||||
statusTab: 'وضعیت',
|
||||
bondTab: 'Bond',
|
||||
nominateTab: 'نامزد',
|
||||
unbondTab: 'Unbond',
|
||||
activeStake: 'Stake فعال',
|
||||
totalBonded: 'کل Bonded',
|
||||
nominations: 'نامزدها',
|
||||
rewardDestination: 'مقصد پاداش',
|
||||
unbondingChunks: 'Unbonding',
|
||||
nominatedValidators: 'اعتبارسنجهای نامزد شده',
|
||||
stakingTip: 'استیک HEZ امتیاز اعتماد را افزایش میدهد. حداقل ۱ ماه استیک کنید.',
|
||||
notStakedYet: 'هنوز استیک نشده',
|
||||
stakeForTrustScore: 'HEZ استیک کنید برای کسب Trust Score',
|
||||
startStaking: 'شروع',
|
||||
yourBalance: 'موجودی شما',
|
||||
currentlyStaked: 'فعلاً Staked',
|
||||
amountHez: 'مقدار (HEZ)',
|
||||
bondWarning: 'استیک کردن ۲۸ روز زمان برداشت دارد.',
|
||||
bonding: 'Bond در حال انجام...',
|
||||
bondExtra: 'افزودن بیشتر',
|
||||
bondButton: 'Bond',
|
||||
bondFirst: 'ابتدا HEZ bond کنید، سپس نامزد کنید',
|
||||
selectValidators: 'اعتبارسنج انتخاب کنید (حداکثر ۱۶): {count}/16',
|
||||
commission: 'کمیسیون:',
|
||||
nominating: 'نامزد کردن...',
|
||||
nominateButton: 'نامزد کن',
|
||||
notStakedUnbond: 'هنوز استیک نکردهاید',
|
||||
unbondWarning: 'Unbond کردن ۲۸ روز طول میکشد. بعداً میتوانید برداشت کنید.',
|
||||
unbondProcessing: 'Unbond در حال انجام...',
|
||||
unbondButton: 'Unbond',
|
||||
bondSuccess: '{amount} HEZ با موفقیت استیک شد!',
|
||||
nominateSuccess: '{count} اعتبارسنج با موفقیت نامزد شدند!',
|
||||
unbondSuccess: '{amount} HEZ unbond شد! (۲۸ روز انتظار)',
|
||||
bondFailed: 'Bond ناموفق',
|
||||
nominateFailed: 'نامزدی ناموفق',
|
||||
unbondFailed: 'Unbond ناموفق',
|
||||
},
|
||||
|
||||
// LP Staking
|
||||
lpStaking: {
|
||||
palletNotReady: 'ماژول Staking آماده نیست',
|
||||
poolsNotLoaded: 'Pool های staking بارگذاری نشدند',
|
||||
noPoolsYet: 'هنوز pool staking نیست',
|
||||
selectPool: 'Pool انتخاب کنید',
|
||||
totalStaked: 'کل Staked',
|
||||
youStaked: 'شما Stake کردید',
|
||||
lpBalance: 'LP موجودی',
|
||||
reward: 'پاداش',
|
||||
stakeTab: 'Stake',
|
||||
unstakeTab: 'Unstake',
|
||||
rewardTab: 'پاداش',
|
||||
amount: 'مقدار ({asset})',
|
||||
balanceLabel: 'موجودی:',
|
||||
stakedLabel: 'Staked:',
|
||||
staking: 'Stake در حال انجام...',
|
||||
stakeButton: 'Stake',
|
||||
unstaking: 'Unstake در حال انجام...',
|
||||
unstakeButton: 'Unstake',
|
||||
pendingRewards: 'پاداشهای در انتظار',
|
||||
claiming: 'دریافت...',
|
||||
claimButton: 'دریافت پاداش',
|
||||
stakeFailed: 'Stake ناموفق',
|
||||
stakeSuccess: 'Stake موفق!',
|
||||
unstakeFailed: 'Unstake ناموفق',
|
||||
unstakeSuccess: 'Unstake موفق!',
|
||||
claimFailed: 'دریافت پاداش ناموفق',
|
||||
claimSuccess: 'پاداش دریافت شد!',
|
||||
},
|
||||
|
||||
// Fees
|
||||
fees: {
|
||||
title: 'افزودن Fee',
|
||||
subtitle: 'HEZ teleport',
|
||||
success: 'موفق!',
|
||||
sentTo: '{amount} HEZ به {chain} ارسال شد',
|
||||
failed: 'ناموفق',
|
||||
tryAgain: 'دوباره تلاش کنید',
|
||||
targetChain: 'زنجیره هدف',
|
||||
forTransfers: 'برای انتقال PEZ',
|
||||
forIdentity: 'برای هویت',
|
||||
minRecommended: '{description} حداقل ۰.۱ HEZ توصیه میشود.',
|
||||
amountHez: 'مقدار (HEZ)',
|
||||
signing: 'امضای تراکنش...',
|
||||
xcmTeleportPending: 'XCM Teleport در حال انجام...',
|
||||
signingButton: 'در حال امضا...',
|
||||
processing: 'در حال پردازش...',
|
||||
sendTo: 'ارسال به {chain}',
|
||||
walletNotConnected: 'کیف پول متصل نیست',
|
||||
apiNotConnected: 'API متصل نیست',
|
||||
enterValidAmount: 'مقدار معتبر وارد کنید',
|
||||
chainNotConnected: 'زنجیره متصل نیست',
|
||||
insufficientBalance: 'موجودی کافی نیست',
|
||||
xcmPalletNotFound: 'ماژول XCM یافت نشد',
|
||||
teleportFailed: 'Teleport ناموفق',
|
||||
errorOccurred: 'خطایی رخ داد',
|
||||
},
|
||||
|
||||
// Tokens
|
||||
tokens: {
|
||||
searchPlaceholder: 'جستجوی token...',
|
||||
addToken: 'افزودن Token',
|
||||
assetIdPlaceholder: 'Asset ID وارد کنید (مثال: 3)',
|
||||
cancel: 'لغو',
|
||||
add: 'افزودن',
|
||||
blockchainConnected: 'Pezkuwichain متصل',
|
||||
connectingBlockchain: 'اتصال به Blockchain...',
|
||||
connectingRpc: 'اتصال به سرور RPC...',
|
||||
tokenNotFound: 'Token یافت نشد',
|
||||
total: 'کل',
|
||||
loadingBalance: 'در حال بارگذاری...',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
networkError: 'اتصال اینترنت نیست. لطفاً اتصال خود را بررسی کنید.',
|
||||
timeout: 'عملیات بیش از حد طول کشید. لطفاً دوباره تلاش کنید.',
|
||||
walletNotFound: 'کیف پول یافت نشد. لطفاً کیف پول بسازید یا بازیابی کنید.',
|
||||
wrongPassword: 'رمز عبور اشتباه. لطفاً دوباره تلاش کنید.',
|
||||
default: 'مشکلی پیش آمد. لطفاً دوباره تلاش کنید.',
|
||||
},
|
||||
|
||||
// Validation
|
||||
validation: {
|
||||
minLength: 'رمز عبور حداقل ۱۲ کاراکتر باشد',
|
||||
needLowercase: 'رمز عبور حداقل ۱ حرف کوچک داشته باشد (a-z)',
|
||||
needUppercase: 'رمز عبور حداقل ۱ حرف بزرگ داشته باشد (A-Z)',
|
||||
needNumber: 'رمز عبور حداقل ۱ عدد داشته باشد (0-9)',
|
||||
needSpecialChar: 'رمز عبور حداقل ۱ کاراکتر خاص داشته باشد (!@#$%...)',
|
||||
weakPassword: 'رمز عبور کافی قوی نیست. رمز عبوری طولانیتر با کاراکترهای متنوع امتحان کنید.',
|
||||
},
|
||||
|
||||
// Time
|
||||
time: {
|
||||
now: 'الان',
|
||||
minutesAgo: '{count} دقیقه پیش',
|
||||
hoursAgo: '{count} ساعت پیش',
|
||||
daysAgo: '{count} روز پیش',
|
||||
},
|
||||
|
||||
// Context
|
||||
context: {
|
||||
walletInitFailed: 'راهاندازی کیف پول ناموفق',
|
||||
rpcDisconnected: 'اتصال RPC قطع شد. در حال اتصال مجدد...',
|
||||
pleaseLoginFirst: 'لطفاً ابتدا وارد شوید',
|
||||
invalidSeedPhrase: 'Seed phrase نامعتبر',
|
||||
connectionFailed: 'اتصال ناموفق',
|
||||
referralStatsError: 'آمار referral بارگذاری نشد',
|
||||
referralApproved: 'Referral تأیید شد! تعداد شما: {count}',
|
||||
wrongPasswordError: 'رمز عبور اشتباه',
|
||||
walletSyncFailed: 'همگامسازی آدرس کیف پول با DB ناموفق',
|
||||
},
|
||||
};
|
||||
|
||||
export default fa;
|
||||
|
||||
@@ -315,6 +315,291 @@ const krd: Translations = {
|
||||
loadingScreen: {
|
||||
loading: 'T\u00ea barkirin...',
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
connected: 'Gir\u00eaday\u00ee',
|
||||
send: 'Bi\u015f\u00eene',
|
||||
receive: 'Werbigire',
|
||||
presaleMessage:
|
||||
'Presale t\u00ea de ye! Z\u00fbtir\u00een demek\u00ea de d\u00ea b\u00eate vekirin.',
|
||||
depositUsdt: 'USDT Z\u00eade Bike',
|
||||
depositUsdtDesc: 'TON, Polkadot an TRC20 ji zinc\u00eer\u00ean din',
|
||||
recentActivity: '\u00c7alakiya Daw\u00ee',
|
||||
history: 'D\u00eerok',
|
||||
refreshTx: 'N\u00fbkirin',
|
||||
loadingTx: 'T\u00ea barkirin...',
|
||||
noRecentTx: 'Transaksiyona daw\u00ee tune',
|
||||
historyAppears: 'D\u00eeroka te d\u00ea li vir xuya bibe',
|
||||
sent: '\u015eandin',
|
||||
received: 'Wergirtin',
|
||||
selectStaking: 'Staking Hilbij\u00eare',
|
||||
validatorNominate: 'Validator nominate bike',
|
||||
trustScorePlus: 'Trust Score +',
|
||||
lpStakeDesc: 'LP token stake bike',
|
||||
pezRewardPlus: 'PEZ Xelat +',
|
||||
goBack: 'Pa\u015fve',
|
||||
},
|
||||
|
||||
// Send
|
||||
send: {
|
||||
back: '\u2190 Pa\u015f',
|
||||
selectToken: 'Token Hilbij\u00eare',
|
||||
whichToken: 'K\u00eejan token bi\u015f\u00eene?',
|
||||
someChainNotConnected: '\u26a0\ufe0f Hinek chain gir\u00eaday\u00ee n\u00eene',
|
||||
transferSuccess: 'Transfer Serket\u00ee!',
|
||||
wasSent: '{amount} {token} hat \u015fandin',
|
||||
done: 'Temam',
|
||||
sendToken: 'Bi\u015f\u00eene {token}',
|
||||
changeToken: 'Biguhere',
|
||||
recipientAddress: 'Navn\u00ee\u015fana Wergir',
|
||||
scanQr: 'QR Bi\u015fox\u00eene',
|
||||
scanQrText: 'Navn\u00ee\u015fana c\u00eezdan\u00ea bi\u015fox\u00eene',
|
||||
qrNotAvailable: 'QR okuyucu v\u00ea platform\u00ea de amade n\u00eene',
|
||||
invalidAddress: 'Navn\u00ee\u015fana derbasdar n\u00eene',
|
||||
amount: 'M\u00eeqdar',
|
||||
balanceLabel: 'Bakiye:',
|
||||
sending: 'T\u00ea \u015fandin...',
|
||||
sendButton: 'Bi\u015f\u00eene',
|
||||
walletNotReady: 'C\u00eezdan amade n\u00eene',
|
||||
selectTokenFirst: 'Token hilbij\u00eare',
|
||||
fillAddressAndAmount: 'Navn\u00ee\u015fan \u00fb m\u00eeqdar biniv\u00eese',
|
||||
insufficientBalance: 'Bakiye t\u00ear\u00ea nake',
|
||||
mainnetApiNotReady: 'Mainnet API amade n\u00eene',
|
||||
assetHubApiNotReady: 'Asset Hub API amade n\u00eene',
|
||||
transferFailed: 'Transfer neserket\u00ee',
|
||||
},
|
||||
|
||||
// Receive
|
||||
receive: {
|
||||
title: 'Werbigire',
|
||||
back: '\u2190 Pa\u015f',
|
||||
qrFailed: 'QR \u00e7\u00eaneb\u00fb',
|
||||
shareAddress:
|
||||
'Ev navn\u00ee\u015fana te ye. Ji bo wergirtina token v\u00ea navn\u00ee\u015fan\u00ea parve bike.',
|
||||
addressCopied: 'Hat kop\u00eekirin!',
|
||||
copyAddress: 'Navn\u00ee\u015fan\u00ea Kop\u00ee Bike',
|
||||
},
|
||||
|
||||
// History
|
||||
history: {
|
||||
title: 'D\u00eeroka Transaksiyonan',
|
||||
back: '\u2190 Pa\u015f',
|
||||
loadingTx: 'T\u00ea barkirin...',
|
||||
noTransactions: 'Transaksiyona tune',
|
||||
historyAppears: 'D\u00eeroka te d\u00ea li vir xuya bibe',
|
||||
sent: '\u015eandin',
|
||||
received: 'Wergirtin',
|
||||
to: 'Ji bo:',
|
||||
from: 'Ji:',
|
||||
},
|
||||
|
||||
// Swap
|
||||
swap: {
|
||||
title: 'Token Swap',
|
||||
fromLabel: 'Ji (From)',
|
||||
toLabel: 'Bo (To)',
|
||||
exchangeRate: 'R\u00eajeya Guherandin\u00ea',
|
||||
noPool: 'Pool tune',
|
||||
swapping: 'T\u00ea guhertin...',
|
||||
noPoolButton: 'Pool Tune',
|
||||
swapButton: 'Swap Bike',
|
||||
swapFailed: 'Swap neserket\u00ee',
|
||||
swapSuccess: 'Swap Serket\u00ee!',
|
||||
balanceLabel: 'Bakiye:',
|
||||
insufficientBalance: 'Bakiye t\u00ear\u00ea nake',
|
||||
},
|
||||
|
||||
// Pools
|
||||
pools: {
|
||||
title: 'Liquidity Pools',
|
||||
connectionError: 'Gir\u00eadan\u00ea \u00e7ewt\u00ee - d\u00eesa bicerb\u00eene',
|
||||
loadingPools: 'T\u00ea barkirin...',
|
||||
noPools: 'Pool tune',
|
||||
back: '\u2190 Pa\u015f',
|
||||
addLiquidity: 'Liquidity Z\u00eade Bike',
|
||||
removeLiquidity: 'Liquidity Derxe',
|
||||
yourPosition: 'Poz\u00eesyona Te',
|
||||
addButton: 'Z\u00eade Bike',
|
||||
removeButton: 'Derxe',
|
||||
reserve: 'Rezerv',
|
||||
lpBalance: 'LP Bakiye:',
|
||||
lpTokenAmount: 'LP Token M\u00eeqdar',
|
||||
amountAuto: '{token} M\u00eeqdar (otomat\u00eek)',
|
||||
adding: 'T\u00ea z\u00eadekirin...',
|
||||
removing: 'T\u00ea derxistin...',
|
||||
addFailed: 'Z\u00eadekirin neserket\u00ee',
|
||||
removeFailed: 'Derxistin neserket\u00ee',
|
||||
invalidLpAmount: 'M\u00eeqdara LP ne derbasdar e',
|
||||
estimatedReturn: 'Texm\u00een\u00ee vegerandin:',
|
||||
success: 'Serket\u00ee!',
|
||||
addedLiquidity: '{amount0} {token0} + {amount1} {token1} hate z\u00eadekirin',
|
||||
removedLiquidity: '{amount} LP token hate vegerandin',
|
||||
},
|
||||
|
||||
// Staking
|
||||
staking: {
|
||||
palletNotFound: 'Staking pallet nehate d\u00eetin',
|
||||
fetchError: 'Staking agah\u00ee nehate stendin',
|
||||
statusTab: 'Durum',
|
||||
bondTab: 'Bond',
|
||||
nominateTab: 'Nominate',
|
||||
unbondTab: 'Unbond',
|
||||
activeStake: 'Akt\u00eef Stake',
|
||||
totalBonded: 'Gi\u015ft\u00ee Bonded',
|
||||
nominations: 'Nominations',
|
||||
rewardDestination: 'Xelat Armanc',
|
||||
unbondingChunks: 'Unbonding',
|
||||
nominatedValidators: 'Nominated Validators',
|
||||
stakingTip:
|
||||
'HEZ stake kirin Trust Score z\u00eade dike. Her\u00ee k\u00eam 1 meh stake bik\u00ee ji bo bonus\u00ea.',
|
||||
notStakedYet: 'H\u00een Stake Nekiriye',
|
||||
stakeForTrustScore: 'HEZ stake bike ji bo Trust Score qezenckirin',
|
||||
startStaking: 'Dest P\u00ea Bike',
|
||||
yourBalance: 'Bakiy\u00ea Te',
|
||||
currentlyStaked: 'Niha Staked',
|
||||
amountHez: 'M\u00eeqdar (HEZ)',
|
||||
bondWarning:
|
||||
'Stake kirin\u00ea pa\u015f\u00ea 28 roj li bend\u00ea ye ji bo veki\u015fandin\u00ea.',
|
||||
bonding: 'T\u00ea bond kirin...',
|
||||
bondExtra: 'Z\u00eade Bike',
|
||||
bondButton: 'Bond Bike',
|
||||
bondFirst: 'P\u00ea\u015f\u00ee HEZ bond bike, pa\u015f\u00ea nominate bike',
|
||||
selectValidators: 'Validator hilbij\u00eare (max 16): {count}/16',
|
||||
commission: 'Kom\u00eesyon:',
|
||||
nominating: 'T\u00ea nominate kirin...',
|
||||
nominateButton: 'Nominate Bike',
|
||||
notStakedUnbond: 'Tu h\u00een stake nekiriye',
|
||||
unbondWarning: 'Unbond kirin 28 roj digire. Pa\u015f\u00ea dikare veki\u015f\u00eene.',
|
||||
unbondProcessing: 'T\u00ea unbond kirin...',
|
||||
unbondButton: 'Unbond Bike',
|
||||
bondSuccess: '{amount} HEZ stake kirin serket\u00ee!',
|
||||
nominateSuccess: '{count} validator nominate kirin serket\u00ee!',
|
||||
unbondSuccess: '{amount} HEZ unbond kirin serket\u00ee! (28 roj li bend\u00ea)',
|
||||
bondFailed: 'Bond neserket\u00ee',
|
||||
nominateFailed: 'Nominate neserket\u00ee',
|
||||
unbondFailed: 'Unbond neserket\u00ee',
|
||||
},
|
||||
|
||||
// LP Staking
|
||||
lpStaking: {
|
||||
palletNotReady: 'Staking palleti amade n\u00eene',
|
||||
poolsNotLoaded: 'Staking pools bar nekirin',
|
||||
noPoolsYet: 'H\u00eaj staking pool tune ne',
|
||||
selectPool: 'Pool Hilbij\u00eare',
|
||||
totalStaked: 'Gi\u015ft\u00ee Staked',
|
||||
youStaked: 'Te Stake Kiriye',
|
||||
lpBalance: 'LP Bakiye',
|
||||
reward: 'Xelat',
|
||||
stakeTab: 'Stake',
|
||||
unstakeTab: 'Unstake',
|
||||
rewardTab: 'Xelat',
|
||||
amount: 'M\u00eeqdar ({asset})',
|
||||
balanceLabel: 'Bakiye:',
|
||||
stakedLabel: 'Staked:',
|
||||
staking: 'T\u00ea stake kirin...',
|
||||
stakeButton: 'Stake Bike',
|
||||
unstaking: 'T\u00ea unstake kirin...',
|
||||
unstakeButton: 'Unstake Bike',
|
||||
pendingRewards: 'Xelat\u00ean li bend\u00ea',
|
||||
claiming: 'T\u00ea stendin...',
|
||||
claimButton: 'Xelatan Bist\u00eene',
|
||||
stakeFailed: 'Stake neserket\u00ee',
|
||||
stakeSuccess: 'Stake serket!',
|
||||
unstakeFailed: 'Unstake neserket\u00ee',
|
||||
unstakeSuccess: 'Unstake serket!',
|
||||
claimFailed: 'Xelat stendin neserket\u00ee',
|
||||
claimSuccess: 'Xelat hat stendin!',
|
||||
},
|
||||
|
||||
// Fees
|
||||
fees: {
|
||||
title: 'Fee Z\u00eade Bike',
|
||||
subtitle: 'HEZ teleport',
|
||||
success: 'Serket\u00ee!',
|
||||
sentTo: '{amount} HEZ bo {chain} hate \u015fandin',
|
||||
failed: 'Neserket\u00ee',
|
||||
tryAgain: 'D\u00eesa Bicerb\u00eene',
|
||||
targetChain: 'Zinc\u00eera Armanc',
|
||||
forTransfers: 'Ji bo PEZ veguheztin',
|
||||
forIdentity: 'Ji bo nasname',
|
||||
minRecommended: '{description} k\u00eam\u00ee 0.1 HEZ t\u00ea p\u00ea\u015fniyarkirin.',
|
||||
amountHez: 'M\u00eeqdar (HEZ)',
|
||||
signing: 'Dan\u00fbstandin\u00ea \u00eemze bikin...',
|
||||
xcmTeleportPending: 'XCM Teleport t\u00ea \u00e7\u00eakirin...',
|
||||
signingButton: 'T\u00ea \u00eemzekirin...',
|
||||
processing: 'T\u00ea \u00e7\u00eakirin...',
|
||||
sendTo: 'Bo {chain} Bi\u015f\u00eene',
|
||||
walletNotConnected: 'Cizdan gir\u00eaday\u00ee n\u00eene',
|
||||
apiNotConnected: 'API gir\u00eaday\u00ee n\u00eene',
|
||||
enterValidAmount: 'M\u00eeqdarek rast biniv\u00eese',
|
||||
chainNotConnected: 'Zinc\u00eer gir\u00eaday\u00ee n\u00eene',
|
||||
insufficientBalance: 'Bakiye t\u00ear\u00ea nake',
|
||||
xcmPalletNotFound: 'XCM pallet nehate d\u00eetin',
|
||||
teleportFailed: 'Teleport neserket\u00ee',
|
||||
errorOccurred: '\u00c7ewtiy\u00eak\u00ee \u00e7\u00eab\u00fb',
|
||||
},
|
||||
|
||||
// Tokens
|
||||
tokens: {
|
||||
searchPlaceholder: 'Token bigere...',
|
||||
addToken: 'Token Z\u00eade Bike',
|
||||
assetIdPlaceholder: 'Asset ID biniv\u00eese (m\u00eenak: 3)',
|
||||
cancel: 'Betal',
|
||||
add: 'Z\u00eade Bike',
|
||||
blockchainConnected: 'Pezkuwichain Gir\u00eaday\u00ee',
|
||||
connectingBlockchain: 'Gir\u00eadana Blockchain...',
|
||||
connectingRpc: 'RPC server\u00ea t\u00ea gir\u00eadan...',
|
||||
tokenNotFound: 'Token nehat d\u00eetin',
|
||||
total: 'Gi\u015ft\u00ee',
|
||||
loadingBalance: 'T\u00ea barkirin...',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
networkError:
|
||||
'T\u00eakiliya \u00eenternet\u00ea tune ye. Ji kerema xwe t\u00eakiliya xwe kontrol bike.',
|
||||
timeout: 'Operasyon z\u00eade dir\u00eaj ki\u015fand. Ji kerema xwe d\u00eesa bicerb\u00eene.',
|
||||
walletNotFound:
|
||||
'Wallet nehate d\u00eetin. Ji kerema xwe wallet \u00e7\u00eake an j\u00ee restore bike.',
|
||||
wrongPassword:
|
||||
'\u015e\u00eefre (password) \u00e7ewt e. Ji kerema xwe d\u00eesa bicerb\u00eene.',
|
||||
default: 'Ti\u015ftek \u00e7ewt \u00e7\u00eab\u00fb. Ji kerema xwe d\u00eesa bicerb\u00eene.',
|
||||
},
|
||||
|
||||
// Validation
|
||||
validation: {
|
||||
minLength: '\u015e\u00eefre (password) her\u00ee k\u00eam 12 t\u00eep be',
|
||||
needLowercase:
|
||||
'\u015e\u00eefre (password) her\u00ee k\u00eam 1 t\u00eepa bi\u00e7\u00fbk hebe (a-z)',
|
||||
needUppercase: '\u015e\u00eefre (password) her\u00ee k\u00eam 1 t\u00eepa mezin hebe (A-Z)',
|
||||
needNumber: '\u015e\u00eefre (password) her\u00ee k\u00eam 1 hejmar hebe (0-9)',
|
||||
needSpecialChar:
|
||||
'\u015e\u00eefre (password) her\u00ee k\u00eam 1 n\u00ee\u015fana taybet hebe (!@#$%...)',
|
||||
weakPassword:
|
||||
'\u015e\u00eefre (password) ne t\u00eara qew\u00ee ye. \u015e\u00eefreyek (password) dir\u00eajtir bi t\u00eep\u00ean c\u00fbrbecu\u0308r bicerb\u00eene.',
|
||||
},
|
||||
|
||||
// Time
|
||||
time: {
|
||||
now: 'Niha',
|
||||
minutesAgo: '{count} deq ber\u00ea',
|
||||
hoursAgo: '{count} saet ber\u00ea',
|
||||
daysAgo: '{count} roj ber\u00ea',
|
||||
},
|
||||
|
||||
// Context
|
||||
context: {
|
||||
walletInitFailed: 'Wallet dest p\u00ea nekir',
|
||||
rpcDisconnected: 'T\u00eakiliya RPC qut b\u00fb. D\u00eesa gir\u00eadan t\u00ea kirin...',
|
||||
pleaseLoginFirst: 'Ji kerema xwe p\u00ea\u015f\u00ee t\u00eakeve',
|
||||
invalidSeedPhrase: 'Seed phrase ne derbasdar e',
|
||||
connectionFailed: 'Gir\u00eadan neserket\u00ee',
|
||||
referralStatsError: 'Referral stats bar nekirin',
|
||||
referralApproved: 'Referral hat pejirand\u00een! Hejmara te: {count}',
|
||||
wrongPasswordError: '\u015e\u00eefre (password) \u00e7ewt e',
|
||||
walletSyncFailed: 'Wallet adresa DB-\u00ea re senkron\u00eeze neb\u00fb',
|
||||
},
|
||||
};
|
||||
|
||||
export default krd;
|
||||
|
||||
@@ -300,6 +300,282 @@ const tr: Translations = {
|
||||
loadingScreen: {
|
||||
loading: 'Yükleniyor...',
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
connected: 'Bağlı',
|
||||
send: 'Gönder',
|
||||
receive: 'Al',
|
||||
presaleMessage: 'Presale yakında! Çok yakında açılacak.',
|
||||
depositUsdt: 'USDT Ekle',
|
||||
depositUsdtDesc: 'TON, Polkadot veya TRC20 ağlarından',
|
||||
recentActivity: 'Son İşlemler',
|
||||
history: 'Geçmiş',
|
||||
refreshTx: 'Yenile',
|
||||
loadingTx: 'Yükleniyor...',
|
||||
noRecentTx: 'Son işlem yok',
|
||||
historyAppears: 'Geçmişiniz burada görünecek',
|
||||
sent: 'Gönderildi',
|
||||
received: 'Alındı',
|
||||
selectStaking: 'Staking Seç',
|
||||
validatorNominate: 'Validator aday göster',
|
||||
trustScorePlus: 'Trust Score +',
|
||||
lpStakeDesc: 'LP token stake et',
|
||||
pezRewardPlus: 'PEZ Ödül +',
|
||||
goBack: 'Geri',
|
||||
},
|
||||
|
||||
// Send
|
||||
send: {
|
||||
back: '← Geri',
|
||||
selectToken: 'Token Seç',
|
||||
whichToken: 'Hangi tokeni göndermek istiyorsun?',
|
||||
someChainNotConnected: '⚠️ Bazı zincirler bağlı değil',
|
||||
transferSuccess: 'Transfer Başarılı!',
|
||||
wasSent: '{amount} {token} gönderildi',
|
||||
done: 'Tamam',
|
||||
sendToken: 'Gönder {token}',
|
||||
changeToken: 'Değiştir',
|
||||
recipientAddress: 'Alıcı Adresi',
|
||||
scanQr: 'QR Tara',
|
||||
scanQrText: 'Cüzdan adresini tara',
|
||||
qrNotAvailable: 'QR okuyucu bu platformda mevcut değil',
|
||||
invalidAddress: 'Geçersiz adres',
|
||||
amount: 'Miktar',
|
||||
balanceLabel: 'Bakiye:',
|
||||
sending: 'Gönderiliyor...',
|
||||
sendButton: 'Gönder',
|
||||
walletNotReady: 'Cüzdan hazır değil',
|
||||
selectTokenFirst: 'Token seçin',
|
||||
fillAddressAndAmount: 'Adres ve miktar girin',
|
||||
insufficientBalance: 'Yetersiz bakiye',
|
||||
mainnetApiNotReady: 'Mainnet API hazır değil',
|
||||
assetHubApiNotReady: 'Asset Hub API hazır değil',
|
||||
transferFailed: 'Transfer başarısız',
|
||||
},
|
||||
|
||||
// Receive
|
||||
receive: {
|
||||
title: 'Al',
|
||||
back: '← Geri',
|
||||
qrFailed: 'QR oluşturulamadı',
|
||||
shareAddress: 'Bu sizin adresiniz. Token almak için bu adresi paylaşın.',
|
||||
addressCopied: 'Kopyalandı!',
|
||||
copyAddress: 'Adresi Kopyala',
|
||||
},
|
||||
|
||||
// History
|
||||
history: {
|
||||
title: 'İşlem Geçmişi',
|
||||
back: '← Geri',
|
||||
loadingTx: 'Yükleniyor...',
|
||||
noTransactions: 'İşlem yok',
|
||||
historyAppears: 'Geçmişiniz burada görünecek',
|
||||
sent: 'Gönderildi',
|
||||
received: 'Alındı',
|
||||
to: 'Kime:',
|
||||
from: 'Kimden:',
|
||||
},
|
||||
|
||||
// Swap
|
||||
swap: {
|
||||
title: 'Token Swap',
|
||||
fromLabel: 'Kimden',
|
||||
toLabel: 'Kime',
|
||||
exchangeRate: 'Döviz Kuru',
|
||||
noPool: 'Pool yok',
|
||||
swapping: 'Takas ediliyor...',
|
||||
noPoolButton: 'Pool Yok',
|
||||
swapButton: 'Swap Yap',
|
||||
swapFailed: 'Swap başarısız',
|
||||
swapSuccess: 'Swap Başarılı!',
|
||||
balanceLabel: 'Bakiye:',
|
||||
insufficientBalance: 'Yetersiz bakiye',
|
||||
},
|
||||
|
||||
// Pools
|
||||
pools: {
|
||||
title: 'Likidite Havuzları',
|
||||
connectionError: 'Bağlantı hatası - tekrar deneyin',
|
||||
loadingPools: 'Yükleniyor...',
|
||||
noPools: 'Pool yok',
|
||||
back: '← Geri',
|
||||
addLiquidity: 'Likidite Ekle',
|
||||
removeLiquidity: 'Likidite Çıkar',
|
||||
yourPosition: 'Pozisyonunuz',
|
||||
addButton: 'Ekle',
|
||||
removeButton: 'Çıkar',
|
||||
reserve: 'Rezerv',
|
||||
lpBalance: 'LP Bakiye:',
|
||||
lpTokenAmount: 'LP Token Miktarı',
|
||||
amountAuto: '{token} Miktar (otomatik)',
|
||||
adding: 'Ekleniyor...',
|
||||
removing: 'Çıkarılıyor...',
|
||||
addFailed: 'Ekleme başarısız',
|
||||
removeFailed: 'Çıkarma başarısız',
|
||||
invalidLpAmount: 'Geçersiz LP miktarı',
|
||||
estimatedReturn: 'Tahmini geri dönüş:',
|
||||
success: 'Başarılı!',
|
||||
addedLiquidity: '{amount0} {token0} + {amount1} {token1} eklendi',
|
||||
removedLiquidity: '{amount} LP token geri alındı',
|
||||
},
|
||||
|
||||
// Staking
|
||||
staking: {
|
||||
palletNotFound: 'Staking modülü bulunamadı',
|
||||
fetchError: 'Staking bilgileri alınamadı',
|
||||
statusTab: 'Durum',
|
||||
bondTab: 'Bond',
|
||||
nominateTab: 'Aday Göster',
|
||||
unbondTab: 'Unbond',
|
||||
activeStake: 'Aktif Stake',
|
||||
totalBonded: 'Toplam Bonded',
|
||||
nominations: 'Adaylıklar',
|
||||
rewardDestination: 'Ödül Hedefi',
|
||||
unbondingChunks: 'Unbonding',
|
||||
nominatedValidators: 'Aday Gösterilen Validatörler',
|
||||
stakingTip: "HEZ stake etmek Trust Score'u artırır. Bonus için en az 1 ay stake edin.",
|
||||
notStakedYet: 'Henüz Stake Yok',
|
||||
stakeForTrustScore: 'Trust Score kazanmak için HEZ stake edin',
|
||||
startStaking: 'Başla',
|
||||
yourBalance: 'Bakiyeniz',
|
||||
currentlyStaked: 'Şu an Staked',
|
||||
amountHez: 'Miktar (HEZ)',
|
||||
bondWarning: 'Stake etme sonrası çekim için 28 gün bekleme süresi vardır.',
|
||||
bonding: 'Bond ediliyor...',
|
||||
bondExtra: 'Daha Ekle',
|
||||
bondButton: 'Bond Et',
|
||||
bondFirst: 'Önce HEZ bond edin, sonra aday gösterin',
|
||||
selectValidators: 'Validatör seçin (max 16): {count}/16',
|
||||
commission: 'Komisyon:',
|
||||
nominating: 'Aday gösteriliyor...',
|
||||
nominateButton: 'Aday Göster',
|
||||
notStakedUnbond: 'Henüz stake yapmadınız',
|
||||
unbondWarning: 'Unbond işlemi 28 gün sürer. Sonra çekilebilir.',
|
||||
unbondProcessing: 'Unbond ediliyor...',
|
||||
unbondButton: 'Unbond Et',
|
||||
bondSuccess: '{amount} HEZ başarıyla stake edildi!',
|
||||
nominateSuccess: '{count} validatör başarıyla aday gösterildi!',
|
||||
unbondSuccess: '{amount} HEZ unbond edildi! (28 gün bekleme)',
|
||||
bondFailed: 'Bond başarısız',
|
||||
nominateFailed: 'Aday gösterme başarısız',
|
||||
unbondFailed: 'Unbond başarısız',
|
||||
},
|
||||
|
||||
// LP Staking
|
||||
lpStaking: {
|
||||
palletNotReady: 'Staking modülü hazır değil',
|
||||
poolsNotLoaded: 'Staking havuzları yüklenemedi',
|
||||
noPoolsYet: 'Henüz staking havuzu yok',
|
||||
selectPool: 'Pool Seç',
|
||||
totalStaked: 'Toplam Staked',
|
||||
youStaked: 'Stake Ettiniz',
|
||||
lpBalance: 'LP Bakiye',
|
||||
reward: 'Ödül',
|
||||
stakeTab: 'Stake',
|
||||
unstakeTab: 'Unstake',
|
||||
rewardTab: 'Ödül',
|
||||
amount: 'Miktar ({asset})',
|
||||
balanceLabel: 'Bakiye:',
|
||||
stakedLabel: 'Staked:',
|
||||
staking: 'Stake ediliyor...',
|
||||
stakeButton: 'Stake Et',
|
||||
unstaking: 'Unstake ediliyor...',
|
||||
unstakeButton: 'Unstake Et',
|
||||
pendingRewards: 'Bekleyen Ödüller',
|
||||
claiming: 'Alınıyor...',
|
||||
claimButton: 'Ödülleri Al',
|
||||
stakeFailed: 'Stake başarısız',
|
||||
stakeSuccess: 'Stake başarılı!',
|
||||
unstakeFailed: 'Unstake başarısız',
|
||||
unstakeSuccess: 'Unstake başarılı!',
|
||||
claimFailed: 'Ödül alma başarısız',
|
||||
claimSuccess: 'Ödüller alındı!',
|
||||
},
|
||||
|
||||
// Fees
|
||||
fees: {
|
||||
title: 'Fee Ekle',
|
||||
subtitle: 'HEZ teleport',
|
||||
success: 'Başarılı!',
|
||||
sentTo: '{amount} HEZ {chain} ağına gönderildi',
|
||||
failed: 'Başarısız',
|
||||
tryAgain: 'Tekrar Dene',
|
||||
targetChain: 'Hedef Zincir',
|
||||
forTransfers: 'PEZ transferi için',
|
||||
forIdentity: 'Kimlik için',
|
||||
minRecommended: '{description} minimum 0.1 HEZ önerilir.',
|
||||
amountHez: 'Miktar (HEZ)',
|
||||
signing: 'İşlem imzalanıyor...',
|
||||
xcmTeleportPending: 'XCM Teleport devam ediyor...',
|
||||
signingButton: 'İmzalanıyor...',
|
||||
processing: 'İşleniyor...',
|
||||
sendTo: '{chain} ağına Gönder',
|
||||
walletNotConnected: 'Cüzdan bağlı değil',
|
||||
apiNotConnected: 'API bağlı değil',
|
||||
enterValidAmount: 'Geçerli bir miktar girin',
|
||||
chainNotConnected: 'Zincir bağlı değil',
|
||||
insufficientBalance: 'Yetersiz bakiye',
|
||||
xcmPalletNotFound: 'XCM modülü bulunamadı',
|
||||
teleportFailed: 'Teleport başarısız',
|
||||
errorOccurred: 'Bir hata oluştu',
|
||||
},
|
||||
|
||||
// Tokens
|
||||
tokens: {
|
||||
searchPlaceholder: 'Token ara...',
|
||||
addToken: 'Token Ekle',
|
||||
assetIdPlaceholder: 'Asset ID girin (ör: 3)',
|
||||
cancel: 'İptal',
|
||||
add: 'Ekle',
|
||||
blockchainConnected: 'Pezkuwichain Bağlı',
|
||||
connectingBlockchain: "Blockchain'e Bağlanıyor...",
|
||||
connectingRpc: 'RPC sunucusuna bağlanıyor...',
|
||||
tokenNotFound: 'Token bulunamadı',
|
||||
total: 'Toplam',
|
||||
loadingBalance: 'Yükleniyor...',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
networkError: 'İnternet bağlantısı yok. Lütfen bağlantınızı kontrol edin.',
|
||||
timeout: 'İşlem çok uzun sürdü. Lütfen tekrar deneyin.',
|
||||
walletNotFound: 'Cüzdan bulunamadı. Lütfen cüzdan oluşturun veya geri yükleyin.',
|
||||
wrongPassword: 'Yanlış şifre. Lütfen tekrar deneyin.',
|
||||
default: 'Bir şeyler yanlış gitti. Lütfen tekrar deneyin.',
|
||||
},
|
||||
|
||||
// Validation
|
||||
validation: {
|
||||
minLength: 'Şifre en az 12 karakter olmalıdır',
|
||||
needLowercase: 'Şifre en az 1 küçük harf içermelidir (a-z)',
|
||||
needUppercase: 'Şifre en az 1 büyük harf içermelidir (A-Z)',
|
||||
needNumber: 'Şifre en az 1 rakam içermelidir (0-9)',
|
||||
needSpecialChar: 'Şifre en az 1 özel karakter içermelidir (!@#$%...)',
|
||||
weakPassword:
|
||||
'Şifre yeterince güçlü değil. Daha uzun ve çeşitli karakterler içeren bir şifre deneyin.',
|
||||
},
|
||||
|
||||
// Time
|
||||
time: {
|
||||
now: 'Şimdi',
|
||||
minutesAgo: '{count} dk önce',
|
||||
hoursAgo: '{count} saat önce',
|
||||
daysAgo: '{count} gün önce',
|
||||
},
|
||||
|
||||
// Context
|
||||
context: {
|
||||
walletInitFailed: 'Cüzdan başlatılamadı',
|
||||
rpcDisconnected: 'RPC bağlantısı kesildi. Yeniden bağlanılıyor...',
|
||||
pleaseLoginFirst: 'Lütfen önce giriş yapın',
|
||||
invalidSeedPhrase: 'Geçersiz seed phrase',
|
||||
connectionFailed: 'Bağlantı başarısız',
|
||||
referralStatsError: 'Referral istatistikleri yüklenemedi',
|
||||
referralApproved: 'Referral onaylandı! Sayınız: {count}',
|
||||
wrongPasswordError: 'Yanlış şifre',
|
||||
walletSyncFailed: 'Cüzdan adresi DB ile senkronize edilemedi',
|
||||
},
|
||||
};
|
||||
|
||||
export default tr;
|
||||
|
||||
@@ -305,6 +305,281 @@ export interface Translations {
|
||||
loadingScreen: {
|
||||
loading: string;
|
||||
};
|
||||
|
||||
// Dashboard (WalletDashboard main view)
|
||||
dashboard: {
|
||||
connected: string;
|
||||
send: string;
|
||||
receive: string;
|
||||
presaleMessage: string;
|
||||
depositUsdt: string;
|
||||
depositUsdtDesc: string;
|
||||
recentActivity: string;
|
||||
history: string;
|
||||
refreshTx: string;
|
||||
loadingTx: string;
|
||||
noRecentTx: string;
|
||||
historyAppears: string;
|
||||
sent: string;
|
||||
received: string;
|
||||
selectStaking: string;
|
||||
validatorNominate: string;
|
||||
trustScorePlus: string;
|
||||
lpStakeDesc: string;
|
||||
pezRewardPlus: string;
|
||||
goBack: string;
|
||||
};
|
||||
|
||||
// Send Tab
|
||||
send: {
|
||||
back: string;
|
||||
selectToken: string;
|
||||
whichToken: string;
|
||||
someChainNotConnected: string;
|
||||
transferSuccess: string;
|
||||
wasSent: string;
|
||||
done: string;
|
||||
sendToken: string;
|
||||
changeToken: string;
|
||||
recipientAddress: string;
|
||||
scanQr: string;
|
||||
scanQrText: string;
|
||||
qrNotAvailable: string;
|
||||
invalidAddress: string;
|
||||
amount: string;
|
||||
balanceLabel: string;
|
||||
sending: string;
|
||||
sendButton: string;
|
||||
walletNotReady: string;
|
||||
selectTokenFirst: string;
|
||||
fillAddressAndAmount: string;
|
||||
insufficientBalance: string;
|
||||
mainnetApiNotReady: string;
|
||||
assetHubApiNotReady: string;
|
||||
transferFailed: string;
|
||||
};
|
||||
|
||||
// Receive Tab
|
||||
receive: {
|
||||
title: string;
|
||||
back: string;
|
||||
qrFailed: string;
|
||||
shareAddress: string;
|
||||
addressCopied: string;
|
||||
copyAddress: string;
|
||||
};
|
||||
|
||||
// History Tab
|
||||
history: {
|
||||
title: string;
|
||||
back: string;
|
||||
loadingTx: string;
|
||||
noTransactions: string;
|
||||
historyAppears: string;
|
||||
sent: string;
|
||||
received: string;
|
||||
to: string;
|
||||
from: string;
|
||||
};
|
||||
|
||||
// Swap Modal
|
||||
swap: {
|
||||
title: string;
|
||||
fromLabel: string;
|
||||
toLabel: string;
|
||||
exchangeRate: string;
|
||||
noPool: string;
|
||||
swapping: string;
|
||||
noPoolButton: string;
|
||||
swapButton: string;
|
||||
swapFailed: string;
|
||||
swapSuccess: string;
|
||||
balanceLabel: string;
|
||||
insufficientBalance: string;
|
||||
};
|
||||
|
||||
// Pools Modal
|
||||
pools: {
|
||||
title: string;
|
||||
connectionError: string;
|
||||
loadingPools: string;
|
||||
noPools: string;
|
||||
back: string;
|
||||
addLiquidity: string;
|
||||
removeLiquidity: string;
|
||||
yourPosition: string;
|
||||
addButton: string;
|
||||
removeButton: string;
|
||||
reserve: string;
|
||||
lpBalance: string;
|
||||
lpTokenAmount: string;
|
||||
amountAuto: string;
|
||||
adding: string;
|
||||
removing: string;
|
||||
addFailed: string;
|
||||
removeFailed: string;
|
||||
invalidLpAmount: string;
|
||||
estimatedReturn: string;
|
||||
success: string;
|
||||
addedLiquidity: string;
|
||||
removedLiquidity: string;
|
||||
};
|
||||
|
||||
// HEZ Staking Modal
|
||||
staking: {
|
||||
palletNotFound: string;
|
||||
fetchError: string;
|
||||
statusTab: string;
|
||||
bondTab: string;
|
||||
nominateTab: string;
|
||||
unbondTab: string;
|
||||
activeStake: string;
|
||||
totalBonded: string;
|
||||
nominations: string;
|
||||
rewardDestination: string;
|
||||
unbondingChunks: string;
|
||||
nominatedValidators: string;
|
||||
stakingTip: string;
|
||||
notStakedYet: string;
|
||||
stakeForTrustScore: string;
|
||||
startStaking: string;
|
||||
yourBalance: string;
|
||||
currentlyStaked: string;
|
||||
amountHez: string;
|
||||
bondWarning: string;
|
||||
bonding: string;
|
||||
bondExtra: string;
|
||||
bondButton: string;
|
||||
bondFirst: string;
|
||||
selectValidators: string;
|
||||
commission: string;
|
||||
nominating: string;
|
||||
nominateButton: string;
|
||||
notStakedUnbond: string;
|
||||
unbondWarning: string;
|
||||
unbondProcessing: string;
|
||||
unbondButton: string;
|
||||
bondSuccess: string;
|
||||
nominateSuccess: string;
|
||||
unbondSuccess: string;
|
||||
bondFailed: string;
|
||||
nominateFailed: string;
|
||||
unbondFailed: string;
|
||||
};
|
||||
|
||||
// LP Staking Modal
|
||||
lpStaking: {
|
||||
palletNotReady: string;
|
||||
poolsNotLoaded: string;
|
||||
noPoolsYet: string;
|
||||
selectPool: string;
|
||||
totalStaked: string;
|
||||
youStaked: string;
|
||||
lpBalance: string;
|
||||
reward: string;
|
||||
stakeTab: string;
|
||||
unstakeTab: string;
|
||||
rewardTab: string;
|
||||
amount: string;
|
||||
balanceLabel: string;
|
||||
stakedLabel: string;
|
||||
staking: string;
|
||||
stakeButton: string;
|
||||
unstaking: string;
|
||||
unstakeButton: string;
|
||||
pendingRewards: string;
|
||||
claiming: string;
|
||||
claimButton: string;
|
||||
stakeFailed: string;
|
||||
stakeSuccess: string;
|
||||
unstakeFailed: string;
|
||||
unstakeSuccess: string;
|
||||
claimFailed: string;
|
||||
claimSuccess: string;
|
||||
};
|
||||
|
||||
// Fund Fees Modal
|
||||
fees: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
success: string;
|
||||
sentTo: string;
|
||||
failed: string;
|
||||
tryAgain: string;
|
||||
targetChain: string;
|
||||
forTransfers: string;
|
||||
forIdentity: string;
|
||||
minRecommended: string;
|
||||
amountHez: string;
|
||||
signing: string;
|
||||
xcmTeleportPending: string;
|
||||
signingButton: string;
|
||||
processing: string;
|
||||
sendTo: string;
|
||||
walletNotConnected: string;
|
||||
apiNotConnected: string;
|
||||
enterValidAmount: string;
|
||||
chainNotConnected: string;
|
||||
insufficientBalance: string;
|
||||
xcmPalletNotFound: string;
|
||||
teleportFailed: string;
|
||||
errorOccurred: string;
|
||||
};
|
||||
|
||||
// Tokens Card
|
||||
tokens: {
|
||||
searchPlaceholder: string;
|
||||
addToken: string;
|
||||
assetIdPlaceholder: string;
|
||||
cancel: string;
|
||||
add: string;
|
||||
blockchainConnected: string;
|
||||
connectingBlockchain: string;
|
||||
connectingRpc: string;
|
||||
tokenNotFound: string;
|
||||
total: string;
|
||||
loadingBalance: string;
|
||||
};
|
||||
|
||||
// Error messages (error-tracking.ts)
|
||||
errors: {
|
||||
networkError: string;
|
||||
timeout: string;
|
||||
walletNotFound: string;
|
||||
wrongPassword: string;
|
||||
default: string;
|
||||
};
|
||||
|
||||
// Password validation (crypto.ts)
|
||||
validation: {
|
||||
minLength: string;
|
||||
needLowercase: string;
|
||||
needUppercase: string;
|
||||
needNumber: string;
|
||||
needSpecialChar: string;
|
||||
weakPassword: string;
|
||||
};
|
||||
|
||||
// Time formatting (utils.ts)
|
||||
time: {
|
||||
now: string;
|
||||
minutesAgo: string;
|
||||
hoursAgo: string;
|
||||
daysAgo: string;
|
||||
};
|
||||
|
||||
// Context messages (WalletContext, ReferralContext, wallet-storage)
|
||||
context: {
|
||||
walletInitFailed: string;
|
||||
rpcDisconnected: string;
|
||||
pleaseLoginFirst: string;
|
||||
invalidSeedPhrase: string;
|
||||
connectionFailed: string;
|
||||
referralStatsError: string;
|
||||
referralApproved: string;
|
||||
wrongPasswordError: string;
|
||||
walletSyncFailed: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type LanguageCode = 'krd' | 'en' | 'tr' | 'ckb' | 'fa' | 'ar';
|
||||
|
||||
+8
-7
@@ -10,6 +10,8 @@
|
||||
* - Version header for future algorithm updates
|
||||
*/
|
||||
|
||||
import { translate } from '@/i18n';
|
||||
|
||||
const SALT_LENGTH = 16;
|
||||
const IV_LENGTH = 12;
|
||||
const VERSION_LENGTH = 1;
|
||||
@@ -162,12 +164,12 @@ export function validatePassword(password: string): {
|
||||
const strength = getPasswordStrength(password);
|
||||
|
||||
if (password.length < 12) {
|
||||
return { valid: false, message: 'Şîfre (password) herî kêm 12 tîp be', entropy, strength };
|
||||
return { valid: false, message: translate('validation.minLength'), entropy, strength };
|
||||
}
|
||||
if (!/[a-z]/.test(password)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Şîfre (password) herî kêm 1 tîpa biçûk hebe (a-z)',
|
||||
message: translate('validation.needLowercase'),
|
||||
entropy,
|
||||
strength,
|
||||
};
|
||||
@@ -175,7 +177,7 @@ export function validatePassword(password: string): {
|
||||
if (!/[A-Z]/.test(password)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Şîfre (password) herî kêm 1 tîpa mezin hebe (A-Z)',
|
||||
message: translate('validation.needUppercase'),
|
||||
entropy,
|
||||
strength,
|
||||
};
|
||||
@@ -183,7 +185,7 @@ export function validatePassword(password: string): {
|
||||
if (!/[0-9]/.test(password)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Şîfre (password) herî kêm 1 hejmar hebe (0-9)',
|
||||
message: translate('validation.needNumber'),
|
||||
entropy,
|
||||
strength,
|
||||
};
|
||||
@@ -191,7 +193,7 @@ export function validatePassword(password: string): {
|
||||
if (!/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Şîfre (password) herî kêm 1 nîşana taybet hebe (!@#$%...)',
|
||||
message: translate('validation.needSpecialChar'),
|
||||
entropy,
|
||||
strength,
|
||||
};
|
||||
@@ -199,8 +201,7 @@ export function validatePassword(password: string): {
|
||||
if (entropy < 60) {
|
||||
return {
|
||||
valid: false,
|
||||
message:
|
||||
'Şîfre (password) ne têra qewî ye. Şîfreyek (password) dirêjtir bi tîpên cûrbecûr biceribîne.',
|
||||
message: translate('validation.weakPassword'),
|
||||
entropy,
|
||||
strength,
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
* - Custom analytics endpoint
|
||||
*/
|
||||
|
||||
import { translate } from '@/i18n';
|
||||
|
||||
export interface ErrorContext {
|
||||
component?: string;
|
||||
action?: string;
|
||||
@@ -118,20 +120,20 @@ export function extractError(caught: unknown): Error {
|
||||
* Format error for user display
|
||||
*/
|
||||
export function formatUserError(error: Error): string {
|
||||
// Map technical errors to user-friendly messages
|
||||
// Map technical errors to translation keys
|
||||
const errorMap: Record<string, string> = {
|
||||
'Network Error': 'Têkiliya înternetê tune ye. Ji kerema xwe têkiliya xwe kontrol bike.',
|
||||
'Failed to fetch': 'Têkiliya înternetê tune ye. Ji kerema xwe têkiliya xwe kontrol bike.',
|
||||
TIMEOUT: 'Operasyon zêde dirêj kişand. Ji kerema xwe dîsa biceribîne.',
|
||||
'Wallet not found': 'Wallet nehate dîtin. Ji kerema xwe wallet çêke an jî restore bike.',
|
||||
'Şîfre (password) çewt e': 'Şîfre (password) çewt e. Ji kerema xwe dîsa biceribîne.',
|
||||
'Network Error': 'errors.networkError',
|
||||
'Failed to fetch': 'errors.networkError',
|
||||
TIMEOUT: 'errors.timeout',
|
||||
'Wallet not found': 'errors.walletNotFound',
|
||||
'Şîfre (password) çewt e': 'errors.wrongPassword',
|
||||
};
|
||||
|
||||
for (const [key, message] of Object.entries(errorMap)) {
|
||||
for (const [key, translationKey] of Object.entries(errorMap)) {
|
||||
if (error.message.includes(key)) {
|
||||
return message;
|
||||
return translate(translationKey);
|
||||
}
|
||||
}
|
||||
|
||||
return 'Tiştek çewt çêbû. Ji kerema xwe dîsa biceribîne.';
|
||||
return translate('errors.default');
|
||||
}
|
||||
|
||||
+15
-5
@@ -1,5 +1,6 @@
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { translate, getCurrentLanguage } from '@/i18n';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
@@ -25,12 +26,21 @@ export function formatDate(date: Date | string): string {
|
||||
const hours = Math.floor(diff / 3600000);
|
||||
const days = Math.floor(diff / 86400000);
|
||||
|
||||
if (minutes < 1) return 'Niha';
|
||||
if (minutes < 60) return `${minutes} deq berê`;
|
||||
if (hours < 24) return `${hours} saet berê`;
|
||||
if (days < 7) return `${days} roj berê`;
|
||||
if (minutes < 1) return translate('time.now');
|
||||
if (minutes < 60) return translate('time.minutesAgo', { count: minutes });
|
||||
if (hours < 24) return translate('time.hoursAgo', { count: hours });
|
||||
if (days < 7) return translate('time.daysAgo', { count: days });
|
||||
|
||||
return d.toLocaleDateString('ku', { day: 'numeric', month: 'short' });
|
||||
const langMap: Record<string, string> = {
|
||||
krd: 'ku',
|
||||
en: 'en',
|
||||
tr: 'tr',
|
||||
ckb: 'ckb',
|
||||
fa: 'fa',
|
||||
ar: 'ar',
|
||||
};
|
||||
const locale = langMap[getCurrentLanguage()] || 'ku';
|
||||
return d.toLocaleDateString(locale, { day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
||||
export function generateId(): string {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { encrypt, decrypt } from './crypto';
|
||||
import { translate } from '@/i18n';
|
||||
|
||||
const STORAGE_KEY = 'pezkuwi_wallet';
|
||||
|
||||
@@ -72,7 +73,7 @@ export async function unlockWallet(password: string): Promise<string> {
|
||||
const mnemonic = await decrypt(wallet.encryptedMnemonic, password);
|
||||
return mnemonic;
|
||||
} catch {
|
||||
throw new Error('Şîfre (password) çewt e');
|
||||
throw new Error(translate('context.wrongPasswordError'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +117,6 @@ export async function syncWalletToSupabase(
|
||||
|
||||
if (error) {
|
||||
console.error('Wallet sync error:', error);
|
||||
throw new Error('Wallet adresa DB-ê re senkronîze nebû');
|
||||
throw new Error(translate('context.walletSyncFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.189",
|
||||
"buildTime": "2026-02-14T08:53:06.213Z",
|
||||
"buildNumber": 1771059186214
|
||||
"version": "1.0.190",
|
||||
"buildTime": "2026-02-14T15:16:08.944Z",
|
||||
"buildNumber": 1771082168945
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user