From 9da348bdf3c2dec56ba41dcbb851dba63fba7535 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Sat, 14 Feb 2026 11:06:14 +0300 Subject: [PATCH] feat: add i18n support with 6 languages (en, tr, krd, ar, fa, ckb) - Add translation system with useTranslation hook and LanguageProvider - Auto-detect language from Telegram user settings - Update all components and sections to use translation keys - Support English, Turkish, Kurdish, Arabic, Persian, Sorani --- package.json | 2 +- src/App.tsx | 16 +- src/components/ErrorBoundary.tsx | 23 +- src/components/LoadingScreen.tsx | 7 +- src/components/P2PModal.tsx | 118 +------- src/components/SocialLinks.tsx | 28 +- src/components/UpdateNotification.tsx | 12 +- src/components/wallet/DepositUSDTModal.tsx | 100 +++---- src/components/wallet/WalletConnect.tsx | 31 +- src/components/wallet/WalletCreate.tsx | 112 ++++--- src/components/wallet/WalletImport.tsx | 56 ++-- src/components/wallet/WalletSetup.tsx | 17 +- src/i18n/index.tsx | 131 +++++++++ src/i18n/translations/ar.ts | 304 +++++++++++++++++++ src/i18n/translations/ckb.ts | 306 +++++++++++++++++++ src/i18n/translations/en.ts | 305 +++++++++++++++++++ src/i18n/translations/fa.ts | 305 +++++++++++++++++++ src/i18n/translations/krd.ts | 320 ++++++++++++++++++++ src/i18n/translations/tr.ts | 305 +++++++++++++++++++ src/i18n/types.ts | 323 +++++++++++++++++++++ src/main.tsx | 21 +- src/sections/Announcements.tsx | 10 +- src/sections/Forum.tsx | 88 +++--- src/sections/Rewards.tsx | 147 +++++----- src/sections/Wallet.tsx | 13 +- src/version.json | 6 +- 26 files changed, 2682 insertions(+), 424 deletions(-) create mode 100644 src/i18n/index.tsx create mode 100644 src/i18n/translations/ar.ts create mode 100644 src/i18n/translations/ckb.ts create mode 100644 src/i18n/translations/en.ts create mode 100644 src/i18n/translations/fa.ts create mode 100644 src/i18n/translations/krd.ts create mode 100644 src/i18n/translations/tr.ts create mode 100644 src/i18n/types.ts diff --git a/package.json b/package.json index b39e2ec..df02286 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pezkuwi-telegram-miniapp", - "version": "1.0.185", + "version": "1.0.186", "type": "module", "description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards", "author": "Pezkuwichain Team", diff --git a/src/App.tsx b/src/App.tsx index b3c13e3..13af9ce 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import { UpdateNotification } from '@/components/UpdateNotification'; import { P2PModal } from '@/components/P2PModal'; import { useAuth } from '@/contexts/AuthContext'; import { useWallet } from '@/contexts/WalletContext'; +import { useTranslation } from '@/i18n'; // Lazy load sections for code splitting const AnnouncementsSection = lazy(() => @@ -35,16 +36,16 @@ type NavId = Section | 'p2p'; interface NavItem { id: NavId; icon: typeof Megaphone; - label: string; + labelKey: string; isExternal?: boolean; } const NAV_ITEMS: NavItem[] = [ - { id: 'announcements', icon: Megaphone, label: 'Ragihandin' }, - { id: 'forum', icon: MessageCircle, label: 'Forum' }, - { id: 'rewards', icon: Gift, label: 'Xelat' }, - { id: 'p2p', icon: ArrowLeftRight, label: 'P2P', isExternal: true }, - { id: 'wallet', icon: Wallet, label: 'Berîk' }, + { id: 'announcements', icon: Megaphone, labelKey: 'nav.announcements' }, + { id: 'forum', icon: MessageCircle, labelKey: 'nav.forum' }, + { id: 'rewards', icon: Gift, labelKey: 'nav.rewards' }, + { id: 'p2p', icon: ArrowLeftRight, labelKey: 'nav.p2p', isExternal: true }, + { id: 'wallet', icon: Wallet, labelKey: 'nav.wallet' }, ]; // P2P Web App URL - Mobile-optimized P2P @@ -55,6 +56,7 @@ export default function App() { const [showP2PModal, setShowP2PModal] = useState(false); const { sessionToken } = useAuth(); const { address } = useWallet(); + const { t } = useTranslation(); // Open P2P in popup with auth params const openP2P = useCallback(() => { @@ -127,7 +129,7 @@ export default function App() { )} > - {item.label} + {t(item.labelKey)} ); })} diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index c803c05..d54dfc2 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -1,6 +1,21 @@ import { Component, ErrorInfo, ReactNode } from 'react'; import { AlertTriangle, RefreshCw } from 'lucide-react'; import { trackError } from '@/lib/error-tracking'; +import krd from '@/i18n/translations/krd'; +import en from '@/i18n/translations/en'; +import tr from '@/i18n/translations/tr'; +import ckb from '@/i18n/translations/ckb'; +import fa from '@/i18n/translations/fa'; +import ar from '@/i18n/translations/ar'; +import type { Translations, LanguageCode } from '@/i18n/types'; + +const translations: Record = { krd, en, tr, ckb, fa, ar }; + +function detectLang(): LanguageCode { + const seg = window.location.pathname.split('/').filter(Boolean)[0]; + if (seg && seg in translations) return seg as LanguageCode; + return 'krd'; +} interface Props { children: ReactNode; @@ -52,16 +67,18 @@ export class ErrorBoundary extends Component {
-

Tiştek çewt çêbû

+

+ {translations[detectLang()].errorBoundary.title} +

- Bibore, pirsgirêkek teknîkî derket. Ji kerema xwe dîsa biceribîne. + {translations[detectLang()].errorBoundary.description}

{import.meta.env.DEV && this.state.error && (
diff --git a/src/components/LoadingScreen.tsx b/src/components/LoadingScreen.tsx index ba03512..678ec3a 100644 --- a/src/components/LoadingScreen.tsx +++ b/src/components/LoadingScreen.tsx @@ -1,10 +1,13 @@ import { Loader2 } from 'lucide-react'; +import { useTranslation } from '@/i18n'; interface LoadingScreenProps { message?: string; } -export function LoadingScreen({ message = 'Tê barkirin...' }: LoadingScreenProps) { +export function LoadingScreen({ message }: LoadingScreenProps) { + const { t } = useTranslation(); + const displayMessage = message ?? t('loadingScreen.loading'); return (
@@ -12,7 +15,7 @@ export function LoadingScreen({ message = 'Tê barkirin...' }: LoadingScreenProp
-

{message}

+

{displayMessage}

); } diff --git a/src/components/P2PModal.tsx b/src/components/P2PModal.tsx index ee8222d..570354c 100644 --- a/src/components/P2PModal.tsx +++ b/src/components/P2PModal.tsx @@ -1,6 +1,6 @@ -import { useState } from 'react'; import { X, ExternalLink, Info } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { useTranslation } from '@/i18n'; interface P2PModalProps { isOpen: boolean; @@ -8,84 +8,8 @@ interface P2PModalProps { onOpenP2P: () => void; } -type Language = 'en' | 'ckb' | 'ku' | 'tr'; - -const LANGUAGES: { code: Language; label: string }[] = [ - { code: 'en', label: 'EN' }, - { code: 'ckb', label: 'سۆرانی' }, - { code: 'ku', label: 'Kurmancî' }, - { code: 'tr', label: 'TR' }, -]; - -const CONTENT: Record< - Language, - { - title: string; - subtitle: string; - firstTime: string; - steps: string[]; - note: string; - button: string; - } -> = { - en: { - title: 'P2P Exchange', - subtitle: 'Trade crypto peer-to-peer', - firstTime: 'First time using P2P?', - steps: [ - 'Click the button below to open the web app', - 'Create an account or log in', - 'Complete the P2P setup process', - 'After setup, you can access P2P directly', - ], - note: 'The web app will open in a new window. Complete the registration process there.', - button: 'Open P2P Platform', - }, - ckb: { - title: 'P2P ئاڵۆگۆڕ', - subtitle: 'ئاڵۆگۆڕی کریپتۆ لە نێوان کەسەکاندا', - firstTime: 'یەکەم جار P2P بەکاردەهێنیت؟', - steps: [ - 'کلیک لە دوگمەی خوارەوە بکە بۆ کردنەوەی ماڵپەڕ', - 'هەژمارێک دروست بکە یان بچۆ ژوورەوە', - 'پرۆسەی دامەزراندنی P2P تەواو بکە', - 'دوای دامەزراندن، دەتوانیت ڕاستەوخۆ بچیتە P2P', - ], - note: 'ماڵپەڕ لە پەنجەرەیەکی نوێ دەکرێتەوە. پرۆسەی تۆمارکردن لەوێ تەواو بکە.', - button: 'کردنەوەی P2P', - }, - ku: { - title: 'P2P Danûstandin', - subtitle: 'Danûstandina krîpto di navbera kesan de', - firstTime: 'Cara yekem P2P bikar tînin?', - steps: [ - 'Li bişkoja jêrîn bikirtînin da ku malpera webê vebike', - 'Hesabek çêbikin an têkevin', - 'Pêvajoya sazkirina P2P temam bikin', - 'Piştî sazkirinê, hûn dikarin rasterast bigihîjin P2P', - ], - note: 'Malpera webê di pencereyek nû de vedibe. Pêvajoya qeydkirinê li wir temam bikin.', - button: 'P2P Veke', - }, - tr: { - title: 'P2P Borsa', - subtitle: 'Kullanıcılar arası kripto alım satım', - firstTime: "P2P'yi ilk kez mi kullanıyorsunuz?", - steps: [ - 'Web uygulamasını açmak için aşağıdaki butona tıklayın', - 'Hesap oluşturun veya giriş yapın', - 'P2P kurulum sürecini tamamlayın', - "Kurulumdan sonra P2P'ye doğrudan erişebilirsiniz", - ], - note: 'Web uygulaması yeni bir pencerede açılacak. Kayıt işlemini orada tamamlayın.', - button: 'P2P Platformunu Aç', - }, -}; - export function P2PModal({ isOpen, onClose, onOpenP2P }: P2PModalProps) { - const [lang, setLang] = useState('en'); - const content = CONTENT[lang]; - const isRTL = lang === 'ckb'; + const { t, isRTL } = useTranslation(); if (!isOpen) return null; @@ -94,6 +18,14 @@ export function P2PModal({ isOpen, onClose, onOpenP2P }: P2PModalProps) { onClose(); }; + // Access steps array directly - t() only works for string values + // We need to get the steps from the translation as individual indexed items + const steps: string[] = []; + for (let i = 0; i < 4; i++) { + const step = t(`p2p.steps.${i}`); + if (step !== `p2p.steps.${i}`) steps.push(step); + } + return (
{/* Header */}
-

{content.title}

+

{t('p2p.title')}

- {/* Language Selector */} -
- {LANGUAGES.map((l) => ( - - ))} -
- {/* Content */}
-

{content.subtitle}

+

{t('p2p.subtitle')}

{/* First Time Info */}
-

{content.firstTime}

+

{t('p2p.firstTime')}

    - {content.steps.map((step, i) => ( + {steps.map((step, i) => (
  1. {i + 1}. {step} @@ -152,7 +66,7 @@ export function P2PModal({ isOpen, onClose, onOpenP2P }: P2PModalProps) {
{/* Note */} -

{content.note}

+

{t('p2p.note')}

{/* Action Button */} @@ -162,7 +76,7 @@ export function P2PModal({ isOpen, onClose, onOpenP2P }: P2PModalProps) { className="w-full flex items-center justify-center gap-2 py-3 px-4 bg-cyan-500 hover:bg-cyan-600 text-white font-medium rounded-xl transition-colors" > - {content.button} + {t('p2p.button')}
diff --git a/src/components/SocialLinks.tsx b/src/components/SocialLinks.tsx index a3ff9c1..c79ba72 100644 --- a/src/components/SocialLinks.tsx +++ b/src/components/SocialLinks.tsx @@ -1,12 +1,13 @@ import { ExternalLink } from 'lucide-react'; import { useTelegram } from '@/hooks/useTelegram'; +import { useTranslation } from '@/i18n'; interface SocialLink { name: string; url: string; icon: string; color: string; - description: string; + descriptionKey: string; } const SOCIAL_LINKS: SocialLink[] = [ @@ -15,61 +16,62 @@ const SOCIAL_LINKS: SocialLink[] = [ url: 'https://www.instagram.com/pezkuwichain', icon: '📸', color: 'from-pink-500 to-purple-600', - description: 'Wêne û Story', + descriptionKey: 'social.instagram', }, { name: 'TikTok', url: 'https://www.tiktok.com/@pezkuwi.chain', icon: '🎵', color: 'from-gray-800 to-gray-900', - description: 'Vîdyoyên kurt', + descriptionKey: 'social.tiktok', }, { name: 'Snapchat', url: 'https://www.snapchat.com/add/pezkuwichain', icon: '👻', color: 'from-yellow-400 to-yellow-500', - description: 'Snap bike!', + descriptionKey: 'social.snapchat', }, { name: 'Telegram', url: 'https://t.me/pezkuwichain', icon: '📢', color: 'from-blue-400 to-blue-600', - description: 'Kanala fermî', + descriptionKey: 'social.telegram', }, { name: 'X (Twitter)', url: 'https://x.com/pezkuwichain', icon: '𝕏', color: 'from-gray-700 to-gray-900', - description: 'Nûçeyên rojane', + descriptionKey: 'social.twitter', }, { name: 'YouTube', url: 'https://www.youtube.com/@SatoshiQazi', icon: '▶️', color: 'from-red-500 to-red-700', - description: 'Vîdyoyên me', + descriptionKey: 'social.youtube', }, { name: 'Facebook', url: 'https://www.facebook.com/people/Pezkuwi-Chain/61587122224932/', icon: '📘', color: 'from-blue-600 to-blue-800', - description: 'Rûpela fermî', + descriptionKey: 'social.facebook', }, { name: 'Discord', url: 'https://discord.gg/Y3VyEC6h8W', icon: '💬', color: 'from-indigo-500 to-purple-600', - description: 'Civaka me', + descriptionKey: 'social.discord', }, ]; export function SocialLinks() { const { openLink, hapticImpact } = useTelegram(); + const { t } = useTranslation(); const handleClick = (url: string) => { hapticImpact('light'); @@ -80,11 +82,9 @@ export function SocialLinks() {

- Me bişopîne + {t('social.followUs')}

-

- Bi me re têkiliyê ragire û nûçeyên herî dawî bistîne! -

+

{t('social.stayConnected')}

{SOCIAL_LINKS.map((link) => ( ))} diff --git a/src/components/UpdateNotification.tsx b/src/components/UpdateNotification.tsx index 5b79e1a..84b4c31 100644 --- a/src/components/UpdateNotification.tsx +++ b/src/components/UpdateNotification.tsx @@ -6,10 +6,12 @@ import { RefreshCw, X } from 'lucide-react'; import { useVersion } from '@/hooks/useVersion'; import { useTelegram } from '@/hooks/useTelegram'; +import { useTranslation } from '@/i18n'; export function UpdateNotification() { const { hasUpdate, forceUpdate, dismissUpdate, currentVersion } = useVersion(); const { hapticImpact } = useTelegram(); + const { t } = useTranslation(); if (!hasUpdate) return null; @@ -31,10 +33,8 @@ export function UpdateNotification() {
-

Guhertoya nû heye!

-

- Ji bo taybetmendiyên nû û rastkirinên ewlehiyê nûve bike. -

+

{t('update.newVersion')}

+

{t('update.description')}

v{currentVersion}

diff --git a/src/components/wallet/DepositUSDTModal.tsx b/src/components/wallet/DepositUSDTModal.tsx index 1c6794f..b4e7402 100644 --- a/src/components/wallet/DepositUSDTModal.tsx +++ b/src/components/wallet/DepositUSDTModal.tsx @@ -18,6 +18,7 @@ import { import { useTelegram } from '@/hooks/useTelegram'; import { useWallet } from '@/contexts/WalletContext'; import { supabase } from '@/lib/supabase'; +import { useTranslation } from '@/i18n'; type Network = 'ton' | 'polkadot' | 'trc20'; @@ -28,7 +29,7 @@ interface NetworkInfo { icon: string; recommended: boolean; fee: number; - feeWarning?: string; + feeWarning?: boolean; explorer: string; minDeposit: number; } @@ -61,7 +62,7 @@ const NETWORKS: NetworkInfo[] = [ icon: '🔴', recommended: false, fee: 3, - feeWarning: 'Mesrefa tora TRC20 bi qasî $3 ye. Em tora TON an Polkadot pêşniyar dikin.', + feeWarning: true, explorer: 'https://tronscan.org/#/transaction/', minDeposit: 10, }, @@ -84,6 +85,7 @@ interface Props { export function DepositUSDTModal({ isOpen, onClose }: Props) { const { hapticImpact, showAlert } = useTelegram(); const { address: localWalletAddress } = useWallet(); + const { t } = useTranslation(); const [selectedNetwork, setSelectedNetwork] = useState('ton'); const [depositCode, setDepositCode] = useState(''); @@ -184,7 +186,7 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { hapticImpact('light'); setTimeout(() => setCopied(null), 2000); } catch { - showAlert('Kopî nekir'); + showAlert(t('deposit.copyFailed')); } }; @@ -204,15 +206,15 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { const getStatusText = (status: string) => { switch (status) { case 'pending': - return 'Li benda'; + return t('deposit.statusPending'); case 'confirming': - return 'Tê pejirandin'; + return t('deposit.statusConfirming'); case 'completed': - return 'Qediya'; + return t('deposit.statusCompleted'); case 'failed': - return 'Neserketî'; + return t('deposit.statusFailed'); case 'expired': - return 'Dema wê derbas bû'; + return t('deposit.statusExpired'); default: return status; } @@ -233,8 +235,8 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) {
-

USDT Depo Bike

-

Bo wUSDT li Asset Hub

+

{t('deposit.title')}

+

{t('deposit.subtitle')}

@@ -256,9 +258,11 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { {showHistory ? ( /* Deposit History */
-

Dîroka Depoyan

+

+ {t('deposit.depositHistory')} +

{deposits.length === 0 ? ( -

Hîn depo tune

+

{t('deposit.noDeposits')}

) : ( deposits.map((deposit) => (
@@ -290,7 +294,7 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { className="text-xs text-blue-400 flex items-center gap-1 mt-2" > - TX bibîne + {t('deposit.viewTx')} )}
@@ -300,14 +304,16 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { onClick={() => setShowHistory(false)} className="w-full py-3 bg-muted rounded-xl text-center" > - Vegere + {t('deposit.goBack')}
) : (
{/* Network Selection */}
- +
{NETWORKS.map((net) => (
@@ -376,19 +384,13 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) {
-

Girîng!

+

{t('deposit.important')}

    -
  • - Kêmtirîn: {network.minDeposit} USDT -
  • - {selectedNetwork !== 'trc20' && ( -
  • Memo/Comment qada de koda xwe binivîse
  • - )} -
  • Tenê USDT bişîne, tokenên din winda dibin
  • +
  • {t('deposit.minimum', { amount: network.minDeposit })}
  • + {selectedNetwork !== 'trc20' &&
  • {t('deposit.memoRequired')}
  • } +
  • {t('deposit.onlyUsdt')}
  • {selectedNetwork === 'trc20' && ( -
  • - $3 masraf dê ji mîqdara we bê kêmkirin -
  • +
  • {t('deposit.trc20Fee')}
  • )}
@@ -399,7 +401,9 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) {
{/* Address */}
- +
{isLoading && selectedNetwork === 'trc20' ? (
@@ -407,7 +411,7 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) {
) : ( - {currentAddress || 'Amade nîne'} + {currentAddress || t('deposit.notAvailable')} )}
-

- ⚠️ Vê kodê di memo de binivîse, wekî din depoya te nayê nas kirin! -

+

{t('deposit.memoWarning')}

)} {selectedNetwork === 'trc20' && ( -

- ✅ Ev navnîşan tenê ya te ye. Memo ne pêwîst e. -

+

{t('deposit.uniqueAddress')}

)}
{/* Steps */}
-

Çawa Depo Bikim?

+

{t('deposit.howToDeposit')}

  1. 1. - Navnîşan kopî bike + {t('deposit.stepCopyAddress')}
  2. {selectedNetwork !== 'trc20' && (
  3. 2. - Memo kodê kopî bike + {t('deposit.stepCopyMemo')}
  4. )}
  5. @@ -485,30 +485,30 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { {selectedNetwork === 'ton' - ? 'Telegram Wallet an cîzdana xwe veke' + ? t('deposit.stepOpenTon') : selectedNetwork === 'polkadot' - ? 'Polkadot cîzdana xwe veke' - : 'TronLink an cîzdana xwe veke'} + ? t('deposit.stepOpenPolkadot') + : t('deposit.stepOpenTrc20')}
  6. {selectedNetwork === 'trc20' ? '3' : '4'}. - USDT bişîne navnîşana jorîn + {t('deposit.stepSendUsdt')}
  7. {selectedNetwork === 'trc20' ? '4' : '5'}. - wUSDT dê di nav çend hûrdeman de li hesabê te be + {t('deposit.stepReceive')}
{/* Processing Time */}

- Dema pêvajoyê: ~1-5 hûrdem + {t('deposit.processingTime')}

)} diff --git a/src/components/wallet/WalletConnect.tsx b/src/components/wallet/WalletConnect.tsx index 2691d15..86338f3 100644 --- a/src/components/wallet/WalletConnect.tsx +++ b/src/components/wallet/WalletConnect.tsx @@ -8,6 +8,7 @@ import { Eye, EyeOff, Wallet, Unlock, Trash2 } from 'lucide-react'; import { useWallet } from '@/contexts/WalletContext'; import { useTelegram } from '@/hooks/useTelegram'; import { formatAddress } from '@/lib/wallet-service'; +import { useTranslation } from '@/i18n'; interface Props { onConnected: () => void; @@ -17,6 +18,7 @@ interface Props { export function WalletConnect({ onConnected, onDelete }: Props) { const { address, connect, error: walletError } = useWallet(); const { hapticImpact, hapticNotification } = useTelegram(); + const { t } = useTranslation(); const [password, setPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); @@ -26,7 +28,7 @@ export function WalletConnect({ onConnected, onDelete }: Props) { const handleConnect = async () => { if (!password) { - setError('Şîfre (password) binivîse'); + setError(t('walletConnect.enterPassword')); return; } @@ -39,7 +41,7 @@ export function WalletConnect({ onConnected, onDelete }: Props) { hapticNotification('success'); onConnected(); } catch (err) { - setError(err instanceof Error ? err.message : 'Şîfre (password) çewt e'); + setError(err instanceof Error ? err.message : t('walletConnect.wrongPassword')); hapticNotification('error'); } finally { setIsLoading(false); @@ -58,11 +60,8 @@ export function WalletConnect({ onConnected, onDelete }: Props) {
-

Wallet Jê Bibe?

-

- Ev çalakî nayê paşvekişandin. Eger seed phrase'ê te tune be, tu nikarî gihîştina - wallet'ê xwe bistînî. -

+

{t('walletConnect.deleteTitle')}

+

{t('walletConnect.deleteDescription')}

@@ -70,13 +69,13 @@ export function WalletConnect({ onConnected, onDelete }: Props) { onClick={() => setShowDeleteConfirm(false)} className="flex-1 py-3 bg-muted rounded-xl font-semibold" > - Betal + {t('common.cancel')}
@@ -89,7 +88,7 @@ export function WalletConnect({ onConnected, onDelete }: Props) {
-

Wallet Veke

+

{t('walletConnect.openWallet')}

{address && (

{formatAddress(address)}

)} @@ -97,7 +96,9 @@ export function WalletConnect({ onConnected, onDelete }: Props) {
- +
setPassword(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleConnect()} className="w-full px-4 py-3 bg-muted rounded-xl pr-12" - placeholder="Şîfre (password) binivîse" + placeholder={t('walletConnect.passwordPlaceholder')} autoFocus /> @@ -143,7 +144,7 @@ export function WalletConnect({ onConnected, onDelete }: Props) { onClick={() => setShowDeleteConfirm(true)} className="w-full py-3 text-red-400 text-sm" > - Wallet jê bibe + {t('walletConnect.deleteWalletLink')}
diff --git a/src/components/wallet/WalletCreate.tsx b/src/components/wallet/WalletCreate.tsx index e4ebaba..eed3c64 100644 --- a/src/components/wallet/WalletCreate.tsx +++ b/src/components/wallet/WalletCreate.tsx @@ -17,6 +17,7 @@ import { } from 'lucide-react'; import { useWallet } from '@/contexts/WalletContext'; import { useTelegram } from '@/hooks/useTelegram'; +import { useTranslation } from '@/i18n'; type Step = 'password' | 'backup' | 'verify' | 'complete'; @@ -34,6 +35,7 @@ interface Props { export function WalletCreate({ onComplete, onBack }: Props) { const { generateNewWallet, confirmWallet, isInitialized } = useWallet(); const { hapticImpact, hapticNotification } = useTelegram(); + const { t } = useTranslation(); const [step, setStep] = useState('password'); const [password, setPassword] = useState(''); @@ -84,12 +86,12 @@ export function WalletCreate({ onComplete, onBack }: Props) { setError(''); if (!isInitialized) { - setError('Wallet service amade nîne. Ji kerema xwe bisekinin.'); + setError(t('walletCreate.walletServiceNotReady')); return; } if (!allPasswordRulesPass) { - setError('Ji kerema xwe hemû şertên şîfre (password) bicîh bînin'); + setError(t('walletCreate.passwordRequirementsNotMet')); hapticNotification('error'); return; } @@ -104,9 +106,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { setStep('backup'); } catch (err) { console.error('Wallet generation error:', err); - setError( - err instanceof Error ? err.message : 'Wallet çênebû. Ji kerema xwe dîsa biceribînin' - ); + setError(err instanceof Error ? err.message : t('walletCreate.walletCreationFailed')); hapticNotification('error'); } }; @@ -122,7 +122,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { // Step 2: Backup - Proceed to verify (only if all conditions checked) const handleBackupContinue = () => { if (!allConditionsChecked) { - setError('Ji kerema xwe hemû şertan bipejirînin'); + setError(t('walletCreate.acceptAllConditions')); return; } @@ -183,7 +183,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { originalWords.every((word, idx) => word === enteredWords[idx]); if (!isCorrect) { - setError('Rêza peyvan ne rast e. Ji kerema xwe dîsa biceribînin'); + setError(t('walletCreate.wrongOrder')); hapticNotification('error'); return; } @@ -197,7 +197,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { hapticNotification('success'); setStep('complete'); } catch (err) { - setError(err instanceof Error ? err.message : 'Wallet çênebû'); + setError(err instanceof Error ? err.message : t('walletCreate.walletCreationFailed')); hapticNotification('error'); } finally { setIsLoading(false); @@ -213,26 +213,26 @@ export function WalletCreate({ onComplete, onBack }: Props) {
-

Şîfre (Password) Diyar Bike

-

- Ev şîfre (password) dê ji bo vekirina wallet'ê were bikaranîn -

+

{t('walletCreate.setPassword')}

+

{t('walletCreate.passwordDescription')}

- +
setPassword(e.target.value)} className="w-full px-4 py-3 bg-muted rounded-xl pr-12" - placeholder="Herî kêm 12 tîp (min 12 characters)" + placeholder={t('walletCreate.passwordPlaceholder')} /> @@ -410,7 +409,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { } className="mt-1 w-5 h-5 accent-primary" /> - Min ev 12 peyv li cihekî ewle nivîsandine + {t('walletCreate.conditionWrittenDown')}
@@ -450,7 +444,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { disabled={!allConditionsChecked} className="w-full py-3 bg-primary text-primary-foreground rounded-xl font-semibold disabled:opacity-50 flex items-center justify-center gap-2" > - {allConditionsChecked ? 'Berdewam' : 'Hemû şertan bipejirînin'} + {allConditionsChecked ? t('common.continue') : t('walletCreate.acceptAllConditions')} {allConditionsChecked && }
@@ -461,24 +455,26 @@ export function WalletCreate({ onComplete, onBack }: Props) { return (
-

Peyvan Verast Bike

+

{t('walletCreate.verifyWords')}

- Ji kerema xwe peyvan bi rêza rast bixin nav qutîkê + {t('walletCreate.verifyDescription')}

{/* Destination area - where user builds the correct order */}
{destinationWords.length === 0 ? ( -

Peyvan li vir bixin...

+

+ {t('walletCreate.dropWordsHere')} +

) : (
{destinationWords.map((word, idx) => ( @@ -525,10 +521,10 @@ export function WalletCreate({ onComplete, onBack }: Props) { className="w-full py-3 bg-primary text-primary-foreground rounded-xl font-semibold disabled:opacity-50" > {isLoading - ? 'Tê tomarkirin...' + ? t('walletCreate.saving') : canVerify - ? 'Verast Bike' - : `${destinationWords.length}/12 peyv`} + ? t('walletCreate.verify') + : t('walletCreate.wordsCount', { count: destinationWords.length })}
); @@ -542,12 +538,12 @@ export function WalletCreate({ onComplete, onBack }: Props) {
-

Wallet Hat Çêkirin!

-

Wallet'ê te amade ye

+

{t('walletCreate.walletCreated')}

+

{t('walletCreate.walletReady')}

-

Navnîşana te

+

{t('walletCreate.yourAddress')}

{address}

@@ -555,7 +551,7 @@ export function WalletCreate({ onComplete, onBack }: Props) { onClick={onComplete} className="w-full py-3 bg-primary text-primary-foreground rounded-xl font-semibold" > - Dest Pê Bike + {t('walletCreate.getStarted')}
); diff --git a/src/components/wallet/WalletImport.tsx b/src/components/wallet/WalletImport.tsx index a2dfe77..7b64d2e 100644 --- a/src/components/wallet/WalletImport.tsx +++ b/src/components/wallet/WalletImport.tsx @@ -8,6 +8,7 @@ import { Eye, EyeOff, ArrowLeft, ArrowRight, Check, AlertTriangle } from 'lucide import { useWallet } from '@/contexts/WalletContext'; import { useTelegram } from '@/hooks/useTelegram'; import { validatePassword } from '@/lib/crypto'; +import { useTranslation } from '@/i18n'; interface Props { onComplete: () => void; @@ -17,6 +18,7 @@ interface Props { export function WalletImport({ onComplete, onBack }: Props) { const { importWallet } = useWallet(); const { hapticImpact, hapticNotification } = useTelegram(); + const { t } = useTranslation(); const [mnemonic, setMnemonic] = useState(''); const [password, setPassword] = useState(''); @@ -49,18 +51,18 @@ export function WalletImport({ onComplete, onBack }: Props) { // Validate mnemonic const words = mnemonic.trim().split(/\s+/); if (words.length !== 12 && words.length !== 24) { - setError('Seed phrase divê 12 an 24 peyv be'); + setError(t('walletImport.seedPhraseInvalid')); return; } // Validate password using crypto.ts rules const passwordValidation = validatePassword(password); if (!passwordValidation.valid) { - setError(passwordValidation.message || 'Şîfre (password) ne derbasdar e'); + setError(passwordValidation.message || t('walletImport.passwordInvalid')); return; } if (password !== confirmPassword) { - setError('Şîfre (password) hev nagirin'); + setError(t('walletImport.passwordsMismatch')); return; } @@ -72,7 +74,7 @@ export function WalletImport({ onComplete, onBack }: Props) { hapticNotification('success'); onComplete(); } catch (err) { - setError(err instanceof Error ? err.message : 'Import neserketî'); + setError(err instanceof Error ? err.message : t('walletImport.importFailed')); hapticNotification('error'); } finally { setIsLoading(false); @@ -83,24 +85,24 @@ export function WalletImport({ onComplete, onBack }: Props) {
-

Wallet Import Bike

-

- Seed phrase'ê wallet'ê xwe yê heyî binivîse -

+

{t('walletImport.title')}

+

{t('walletImport.description')}

- +