diff --git a/src/components/common/theme-demo.stories.tsx b/src/components/common/theme-demo.stories.tsx index 732ff808..bbe15bd0 100644 --- a/src/components/common/theme-demo.stories.tsx +++ b/src/components/common/theme-demo.stories.tsx @@ -5,32 +5,32 @@ import { LoadingSpinner } from './loading-spinner'; import { EmptyState } from './empty-state'; function ThemeDemoComponent() { - const { t, i18n } = useTranslation(); + const { t, i18n } = useTranslation(['common', 'wallet', 'transaction']); return (
-

{t('wallet.myWallet')}

-

{t('common.loading')}

+

{t('wallet:myWallet')}

+

{t('loading')}

- {t('common.loading')}... + {t('loading')}...
- {t('wallet.transfer')} + {t('wallet:transfer')} - {t('wallet.receive')} + {t('wallet:receive')}
-

{t('transaction.send')}

-

{t('transaction.pending')}

+

{t('transaction:send')}

+

{t('transaction:pending')}

@@ -72,14 +72,14 @@ export const Default: Story = {}; export const WithEmptyState: Story = { render: () => { - const { t } = useTranslation(); + const { t } = useTranslation(['token', 'empty']); return ( - {t('token.addToken')} + {t('addToken')} } /> @@ -89,13 +89,13 @@ export const WithEmptyState: Story = { export const TransactionStates: Story = { render: () => { - const { t } = useTranslation(); + const { t } = useTranslation('transaction'); return (
{(['send', 'receive', 'swap', 'stake'] as const).map((type) => (
- {t(`transaction.${type}`)} - {t('transaction.confirmed')} + {t(type)} + {t('confirmed')}
))}
@@ -105,11 +105,11 @@ export const TransactionStates: Story = { export const SecurityLabels: Story = { render: () => { - const { t } = useTranslation(); + const { t } = useTranslation('security'); return (
-

{t('security.password')}

+

{t('password')}

{(['weak', 'medium', 'strong'] as const).map((level) => ( - {t(`security.strength.${level}`)} + {t(`strength.${level}`)} ))}
-

{t('security.mnemonic')}

-

{t('security.copyMnemonic')}

+

{t('mnemonic')}

+

{t('copyMnemonic')}

); diff --git a/src/components/onboarding/key-type-selector.tsx b/src/components/onboarding/key-type-selector.tsx index d52f4f62..3e5a85e6 100644 --- a/src/components/onboarding/key-type-selector.tsx +++ b/src/components/onboarding/key-type-selector.tsx @@ -33,13 +33,13 @@ const OPTIONS: Array<{ ]; export function KeyTypeSelector({ value, onChange, disabled = false, className }: KeyTypeSelectorProps) { - const { t } = useTranslation(['onboarding', 'common']); + const { t } = useTranslation('onboarding'); return (
-
{t('onboarding:keyType.title')}
+
{t('keyType.title')}
-
+
{OPTIONS.map((option) => { const isSelected = value === option.value; return ( @@ -60,8 +60,8 @@ export function KeyTypeSelector({ value, onChange, disabled = false, className } >
-
{t(`onboarding:${option.titleKey}`)}
-
{t(`onboarding:${option.descKey}`)}
+
{t(option.titleKey as 'keyType.mnemonic' | 'keyType.arbitrary')}
+
{t(option.descKey as 'keyType.mnemonicDesc' | 'keyType.arbitraryDesc')}
{option.tags.map((tag) => ( {ui}) @@ -31,7 +31,7 @@ describe('calculateStrength', () => { }) describe('PasswordInput', () => { - const placeholder = testI18n.t('security:passwordConfirm.placeholder') + const placeholder = '请输入密码' it('renders password input', () => { renderWithProviders() @@ -41,15 +41,15 @@ describe('PasswordInput', () => { it('toggles password visibility', async () => { renderWithProviders() const input = screen.getByPlaceholderText(placeholder) - const toggleButton = screen.getByRole('button', { name: testI18n.t('a11y.showPassword') }) + const toggleButton = screen.getByRole('button', { name: '显示' }) expect(input).toHaveAttribute('type', 'password') await userEvent.click(toggleButton) expect(input).toHaveAttribute('type', 'text') - expect(screen.getByRole('button', { name: testI18n.t('a11y.hidePassword') })).toBeInTheDocument() + expect(screen.getByRole('button', { name: '隐藏' })).toBeInTheDocument() - await userEvent.click(screen.getByRole('button', { name: testI18n.t('a11y.hidePassword') })) + await userEvent.click(screen.getByRole('button', { name: '隐藏' })) expect(input).toHaveAttribute('type', 'password') }) @@ -58,7 +58,7 @@ describe('PasswordInput', () => { const input = document.querySelector('input')! await userEvent.type(input, 'test') - const strengthLabel = testI18n.t('common:passwordStrength') + const strengthLabel = '强度' await waitFor(() => { const matches = screen.getAllByText( (_content, node) => node?.textContent?.includes(strengthLabel) ?? false, diff --git a/src/components/security/password-input.tsx b/src/components/security/password-input.tsx index 5337eb0c..d91b64cd 100644 --- a/src/components/security/password-input.tsx +++ b/src/components/security/password-input.tsx @@ -37,7 +37,7 @@ const PasswordInput = forwardRef( const [visible, setVisible] = useState(false); const [strength, setStrength] = useState('weak'); const [hasValue, setHasValue] = useState(!!value); - const { t } = useTranslation(); + const { t } = useTranslation('common'); const handleChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; @@ -95,7 +95,7 @@ const PasswordInput = forwardRef(

{t('a11y.passwordStrength', { strength: config.label })}

{error || isErrorAnimating ? (

- {t('security:patternLock.error')} + {t('patternLock.error')}

) : selectedNodes.length === 0 ? (

- {t('security:patternLock.hint', { min: minPoints })} + {t('patternLock.hint', { min: minPoints })}

) : selectedNodes.length < minPoints ? (

- {t('security:patternLock.needMore', { current: selectedNodes.length, min: minPoints })} + {t('patternLock.needMore', { current: selectedNodes.length, min: minPoints })}

) : success ? (

- {t('security:patternLock.success')} + {t('patternLock.success')}

) : (

- {t('security:patternLock.valid', { count: selectedNodes.length })} + {t('patternLock.valid', { count: selectedNodes.length })}

)}
@@ -526,7 +526,7 @@ export function PatternLock({ data-testid={baseTestId ? `${baseTestId}-clear` : undefined} className="mx-auto block text-sm text-muted-foreground hover:text-foreground transition-colors" > - {t('security:patternLock.clear')} + {t('patternLock.clear')} )}
diff --git a/src/components/token/token-item.tsx b/src/components/token/token-item.tsx index 6547d4c2..bd0219db 100644 --- a/src/components/token/token-item.tsx +++ b/src/components/token/token-item.tsx @@ -36,7 +36,7 @@ interface TokenItemProps { export function TokenItem({ token, onClick, showChange = false, loading = false, className }: TokenItemProps) { const isClickable = !!onClick; - const { t } = useTranslation(); + const { t } = useTranslation(['currency', 'common']); const currency = useCurrency(); const shouldFetchRate = token.fiatValue !== undefined && currency !== 'USD'; @@ -59,10 +59,10 @@ export function TokenItem({ token, onClick, showChange = false, loading = false, const exchangeStatusMessage = shouldFetchRate && !canConvert ? exchangeRateError - ? t('currency:exchange.error') + ? t('exchange.error') : exchangeRateLoading - ? t('currency:exchange.loading') - : t('currency:exchange.unavailable') + ? t('exchange.loading') + : t('exchange.unavailable') : null; return ( @@ -71,7 +71,7 @@ export function TokenItem({ token, onClick, showChange = false, loading = false, tabIndex={isClickable ? 0 : undefined} onClick={onClick} onKeyDown={isClickable ? (e) => e.key === 'Enter' && onClick?.() : undefined} - aria-label={isClickable ? t('a11y.tokenDetails', { token: token.symbol }) : undefined} + aria-label={isClickable ? t('common:a11y.tokenDetails', { token: token.symbol }) : undefined} className={cn( '@container flex items-center gap-3 rounded-xl p-3 transition-colors', isClickable && 'hover:bg-muted/50 active:bg-muted cursor-pointer', diff --git a/src/components/transfer/address-input.tsx b/src/components/transfer/address-input.tsx index 7c6a9b24..c79c0ad6 100644 --- a/src/components/transfer/address-input.tsx +++ b/src/components/transfer/address-input.tsx @@ -41,7 +41,7 @@ const AddressInput = forwardRef( const [showDropdown, setShowDropdown] = useState(false); const [selectedIndex, setSelectedIndex] = useState(-1); const containerRef = useRef(null); - const { t } = useTranslation(); + const { t } = useTranslation('common'); const errorId = useId(); const listboxId = useId(); @@ -149,7 +149,7 @@ const AddressInput = forwardRef( onBlur={() => setTimeout(() => setFocused(false), 150)} onKeyDown={handleKeyDown} className="placeholder:text-muted-foreground min-w-0 flex-1 bg-transparent font-mono text-sm outline-none" - placeholder={t('common:addressPlaceholder')} + placeholder={t('addressPlaceholder')} autoComplete="off" autoCapitalize="off" autoCorrect="off" @@ -182,7 +182,7 @@ const AddressInput = forwardRef( aria-label={t('a11y.paste')} > - {t('common:paste')} + {t('paste')}
diff --git a/src/components/wallet/wallet-card.tsx b/src/components/wallet/wallet-card.tsx index 1520d3fb..4fb5f3dc 100644 --- a/src/components/wallet/wallet-card.tsx +++ b/src/components/wallet/wallet-card.tsx @@ -27,7 +27,7 @@ export function WalletCard({ onReceive, className, }: WalletCardProps) { - const { t } = useTranslation('wallet') + const { t } = useTranslation(['wallet', 'common']) return (
- {t('common:transfer')} + {t('transfer')} )} {onReceive && ( @@ -94,7 +94,7 @@ export function WalletCard({ onClick={onReceive} className="flex-1 py-2 px-4 bg-white/20 rounded-full text-sm font-medium hover:bg-white/30 transition-colors @xs:py-2.5 @xs:text-base" > - {t('common:receive')} + {t('receive')} )}
diff --git a/src/components/wallet/wallet-selector.tsx b/src/components/wallet/wallet-selector.tsx index 2875424a..bd19558d 100644 --- a/src/components/wallet/wallet-selector.tsx +++ b/src/components/wallet/wallet-selector.tsx @@ -73,7 +73,7 @@ function WalletItem({ wallet, isSelected, onSelect, notBackedUpLabel }: WalletIt * Wallet selector component for switching between multiple wallets */ export function WalletSelector({ wallets, selectedId, onSelect, onClose, className }: WalletSelectorProps) { - const { t } = useTranslation('common'); + const { t } = useTranslation(['common', 'wallet']); const handleSelect = (wallet: WalletInfo) => { onSelect?.(wallet); @@ -97,7 +97,7 @@ export function WalletSelector({ wallets, selectedId, onSelect, onClose, classNa wallet={wallet} isSelected={wallet.id === selectedId} onSelect={() => handleSelect(wallet)} - notBackedUpLabel={t('wallet:notBackedUp')} + notBackedUpLabel={t('notBackedUp')} /> ))}
diff --git a/src/i18n/i18next.d.ts b/src/i18n/i18next.d.ts new file mode 100644 index 00000000..9af7ffd9 --- /dev/null +++ b/src/i18n/i18next.d.ts @@ -0,0 +1,48 @@ +import 'i18next' + +import type authorize from './locales/zh-CN/authorize.json' +import type common from './locales/zh-CN/common.json' +import type currency from './locales/zh-CN/currency.json' +import type dweb from './locales/zh-CN/dweb.json' +import type empty from './locales/zh-CN/empty.json' +import type error from './locales/zh-CN/error.json' +import type guide from './locales/zh-CN/guide.json' +import type home from './locales/zh-CN/home.json' +import type migration from './locales/zh-CN/migration.json' +import type notification from './locales/zh-CN/notification.json' +import type onboarding from './locales/zh-CN/onboarding.json' +import type scanner from './locales/zh-CN/scanner.json' +import type security from './locales/zh-CN/security.json' +import type settings from './locales/zh-CN/settings.json' +import type staking from './locales/zh-CN/staking.json' +import type time from './locales/zh-CN/time.json' +import type token from './locales/zh-CN/token.json' +import type transaction from './locales/zh-CN/transaction.json' +import type wallet from './locales/zh-CN/wallet.json' + +declare module 'i18next' { + interface CustomTypeOptions { + defaultNS: 'common' + resources: { + authorize: typeof authorize + common: typeof common + currency: typeof currency + dweb: typeof dweb + empty: typeof empty + error: typeof error + guide: typeof guide + home: typeof home + migration: typeof migration + notification: typeof notification + onboarding: typeof onboarding + scanner: typeof scanner + security: typeof security + settings: typeof settings + staking: typeof staking + time: typeof time + token: typeof token + transaction: typeof transaction + wallet: typeof wallet + } + } +} diff --git a/src/i18n/locales/ar/authorize.json b/src/i18n/locales/ar/authorize.json index 4e7ecccb..c578bde2 100644 --- a/src/i18n/locales/ar/authorize.json +++ b/src/i18n/locales/ar/authorize.json @@ -55,5 +55,8 @@ "authFailed": "Authorization failed", "patternIncorrect": "Incorrect pattern", "timeout": "Request timed out" + }, + "passwordConfirm": { + "title": "Confirm Password" } } diff --git a/src/i18n/locales/ar/common.json b/src/i18n/locales/ar/common.json index f91129fd..f14b3f95 100644 --- a/src/i18n/locales/ar/common.json +++ b/src/i18n/locales/ar/common.json @@ -298,6 +298,7 @@ "userAgreement": "User agreement", "userAvatorAndNikename": "User avatar and nickname", "validationSucceeded": "Validation succeeded", + "verifying": "[MISSING:ar] 验证中...", "version": "Version", "versionUpgrade": "version upgrade", "viewInBrowser": "View in browser", diff --git a/src/i18n/locales/ar/security.json b/src/i18n/locales/ar/security.json index a5491794..448ae2d4 100644 --- a/src/i18n/locales/ar/security.json +++ b/src/i18n/locales/ar/security.json @@ -1,141 +1,147 @@ { - "patternLock": { - "gridLabel": "{{size}}x{{size}} قفل النمط", - "nodeLabel": "عقدة في الصف {{row}}، العمود {{col}}{{order, number}}", - "hint": "قم بتوصيل {{min}} نقاط على الأقل", - "needMore": "تم توصيل {{current}} نقاط، تحتاج {{min, number}} أخرى", - "error": "النمط غير متطابق، حاول مرة أخرى", - "success": "النمط صحيح", - "valid": "تم توصيل {{count}} نقاط", - "clear": "مسح النمط", - "reset": "إعادة تعيين", - "setTitle": "تعيين قفل المحفظة", - "setDesc": "ارسم نمطاً كقفل للمحفظة لحماية محفظتك", - "confirmTitle": "تأكيد قفل المحفظة", - "confirmDesc": "ارسم النمط مرة أخرى للتأكيد", - "unlockTitle": "فتح المحفظة", - "unlockDesc": "ارسم نمط قفل المحفظة", - "mismatch": "الأنماط غير متطابقة، حاول مرة أخرى" - }, + "applicationLock": "قفل التطبيق", + "areYouSureYouWantToSkipBackup": "هل أنت متأكد من تخطي النسخ الاحتياطي؟", + "backupMnemonic": "نسخ العبارة السرية احتياطياً", + "backupNow": "نسخ احتياطي الآن", "backupTips": { - "title": "نسخ العبارة السرية احتياطياً", "description": "العبارة السرية هي الطريقة الوحيدة لاستعادة محفظتك، يرجى الحفاظ عليها بأمان", + "proceed": "فهمت، ابدأ النسخ الاحتياطي", + "skip": "النسخ الاحتياطي لاحقاً", "tips": { - "privacy": { - "title": "تأكد من الخصوصية", - "description": "اعرض العبارة السرية في بيئة خاصة لمنع الآخرين من رؤيتها" - }, "noScreenshot": { - "title": "لا لقطات شاشة", - "description": "لا تحفظ العبارة السرية إلكترونياً، اكتبها بدلاً من ذلك" + "description": "لا تحفظ العبارة السرية إلكترونياً، اكتبها بدلاً من ذلك", + "title": "لا لقطات شاشة" + }, + "privacy": { + "description": "اعرض العبارة السرية في بيئة خاصة لمنع الآخرين من رؤيتها", + "title": "تأكد من الخصوصية" }, "safekeeping": { - "title": "تخزين آمن", - "description": "احتفظ بالعبارة السرية في مكان آمن، فقدانها سيؤدي إلى فقدان الأصول" + "description": "احتفظ بالعبارة السرية في مكان آمن، فقدانها سيؤدي إلى فقدان الأصول", + "title": "تخزين آمن" } }, - "proceed": "فهمت، ابدأ النسخ الاحتياطي", - "skip": "النسخ الاحتياطي لاحقاً" - }, - "walletLock": { - "unlockTitle": "فتح المحفظة", - "unlockDesc": "ارسم نمط قفل المحفظة", - "verifying": "جاري التحقق...", - "error": "النمط غير صحيح، حاول مرة أخرى", - "biometric": "استخدام البصمة", - "cancel": "إلغاء" - }, - "twoStepSecret": { - "setTitle": "تعيين كلمة مرور الأمان", - "tooShort": "كلمة مرور الأمان يجب أن تكون 6 أحرف على الأقل", - "lengthHint": "يُنصح بـ 6 أحرف على الأقل", - "notMatch": "كلمات المرور غير متطابقة", - "setFailed": "فشل تعيين كلمة مرور الأمان، حاول مرة أخرى", - "setSuccess": "تم تعيين كلمة مرور الأمان بنجاح", - "setSuccessDesc": "ستحتاج إلى إدخال كلمة مرور الأمان لكل عملية تحويل", - "broadcastSuccess": "تم البث بنجاح", - "waitingConfirm": "تم بث المعاملة، في انتظار التأكيد...", - "txConfirmed": "تم تأكيد المعاملة", - "feeInfo": "تعيين كلمة مرور الأمان يتطلب رسوم الشبكة", - "inputDesc": "قم بتعيين كلمة مرور أمان لحماية تحويلاتك", - "inputPlaceholder": "أدخل كلمة مرور الأمان", - "confirmDesc": "أدخل كلمة مرور الأمان مرة أخرى للتأكيد", - "confirmPlaceholder": "تأكيد كلمة مرور الأمان", - "walletLockDesc": "افتح المحفظة للتأكيد", - "walletLockPlaceholder": "أدخل قفل المحفظة", - "alreadySet": "كلمة مرور الأمان معينة بالفعل لهذا العنوان", - "notSet": "لم يتم تعيين كلمة مرور الأمان", - "setup": "تعيين كلمة مرور الأمان" + "title": "نسخ العبارة السرية احتياطياً" }, + "backupYourMnemonicNow": "نسخ العبارة السرية احتياطياً الآن", + "clickHereToQueryTheTransferProgressThroughTheBlockBrowser": "انقر هنا للتحقق من تفاصيل التحويل في مستكشف البلوكتشين.", + "clickOnTheWordsToArrangeThemInMnemonicOrder": "انقر على الكلمات لترتيبها بالترتيب الصحيح.", + "confirmMnemonic": "تأكيد العبارة السرية", + "confirmToTurnOffFingerprintPayment": "تأكيد إيقاف الدفع بالبصمة؟", + "copyMnemonic": "نسخ العبارة السرية", + "doNotDiscloseOrShareTheMnemonicPhraseWithOthers": "لا تكشف أو تشارك العبارة السرية مع الآخرين.", + "enterTheMnemonicSeparatedBySpaces": "أدخل العبارة السرية، مفصولة بمسافات", + "enterTheTransactionPassword": "أدخل كلمة مرور المعاملة", + "exportMnemonic": "تصدير العبارة السرية", + "fingerprintAuthenticationLogin": "تسجيل الدخول بالبصمة", + "fingerprintPayment": "الدفع بالبصمة", + "fingerprintUnlock": "فتح بالبصمة", + "fingerprintVerificationFailed": "فشل التحقق من البصمة", + "giveUpBackup": "التخلي عن النسخ الاحتياطي", + "ifYouLoseYourMnemonicPhraseYourAssetsAreLostForever": "إذا فقدت العبارة السرية، ستفقد أصولك للأبد.", + "inTheNextStepYouCanSeeTheMnemonicWordsUsed": "في الخطوة التالية، سترى كلمات العبارة السرية", + "loginWithFingerprintVerification": "تسجيل الدخول بالبصمة", + "mnemonic": "العبارة السرية", "mnemonicConfirm": { - "selected": "محدد {{current}}/{{total}}", - "undo": "تراجع", - "hint": "انقر على الكلمات أدناه بالترتيب الصحيح", + "clearAll": "مسح الكل", "error": "ترتيب العبارة السرية غير صحيح، يرجى المحاولة مرة أخرى", - "success": "تم التحقق من العبارة السرية بنجاح", + "hint": "انقر على الكلمات أدناه بالترتيب الصحيح", "reset": "إعادة تعيين", - "clearAll": "مسح الكل" - }, - "mnemonicInput": { - "entered": "تم إدخال {{filled}}/{{total}} كلمات", - "clear": "مسح" + "selected": "محدد {{current}}/{{total}}", + "success": "تم التحقق من العبارة السرية بنجاح", + "undo": "تراجع" }, + "mnemonicCopied": "تم نسخ العبارة السرية", "mnemonicDisplay": { - "hidden": "••••••", "copied": "تم النسخ", - "copy": "نسخ العبارة السرية" + "copy": "نسخ العبارة السرية", + "hidden": "••••••" }, - "mnemonic": "العبارة السرية", - "confirmMnemonic": "تأكيد العبارة السرية", - "copyMnemonic": "نسخ العبارة السرية", - "backupMnemonic": "نسخ العبارة السرية احتياطياً", - "backupNow": "نسخ احتياطي الآن", - "noBackup": "لا نسخة احتياطية", - "skipBackup": "تخطي النسخ الاحتياطي", - "giveUpBackup": "التخلي عن النسخ الاحتياطي", - "areYouSureYouWantToSkipBackup": "هل أنت متأكد من تخطي النسخ الاحتياطي؟", "mnemonicImport": "استيراد العبارة السرية", - "mnemonicCopied": "تم نسخ العبارة السرية", - "yourMnemonic": "العبارة السرية الخاصة بك", - "doNotDiscloseOrShareTheMnemonicPhraseWithOthers": "لا تكشف أو تشارك العبارة السرية مع الآخرين.", - "ifYouLoseYourMnemonicPhraseYourAssetsAreLostForever": "إذا فقدت العبارة السرية، ستفقد أصولك للأبد.", - "enterTheMnemonicSeparatedBySpaces": "أدخل العبارة السرية، مفصولة بمسافات", + "mnemonicInpitErrorPleaseReEnter": "خطأ في إدخال العبارة السرية، أعد الإدخال", + "mnemonicInput": { + "clear": "مسح", + "entered": "تم إدخال {{filled}}/{{total}} كلمات" + }, + "noBackup": "لا نسخة احتياطية", + "password": "[MISSING:ar] 密码", + "passwordConfirm": { + "verifying": "[MISSING:ar] 验证中..." + }, + "patternLock": { + "clear": "مسح النمط", + "confirmDesc": "ارسم النمط مرة أخرى للتأكيد", + "confirmTitle": "تأكيد قفل المحفظة", + "error": "النمط غير متطابق، حاول مرة أخرى", + "gridLabel": "{{size}}x{{size}} قفل النمط", + "hint": "قم بتوصيل {{min}} نقاط على الأقل", + "mismatch": "الأنماط غير متطابقة، حاول مرة أخرى", + "needMore": "تم توصيل {{current}} نقاط، تحتاج {{min, number}} أخرى", + "nodeLabel": "عقدة في الصف {{row}}، العمود {{col}}{{order, number}}", + "reset": "إعادة تعيين", + "setDesc": "ارسم نمطاً كقفل للمحفظة لحماية محفظتك", + "setTitle": "تعيين قفل المحفظة", + "success": "النمط صحيح", + "unlockDesc": "ارسم نمط قفل المحفظة", + "unlockTitle": "فتح المحفظة", + "valid": "تم توصيل {{count}} نقاط" + }, + "pleaseBackupYourWalletNow": "يرجى نسخ محفظتك احتياطياً الآن", + "pleaseGoToThePhoneSystemToRegisterFingerprints": "يرجى الذهاب إلى نظام الهاتف لتسجيل البصمات", + "pleaseKeepInMindAfterSettingTheTransactionPassword": "يرجى تذكر كلمة مرور المعاملة بعد تعيينها", + "pleaseVerifyTheFingerprint": "يرجى التحقق من البصمة", + "queryInBlockchainExplorer": "استعلام في مستكشف البلوكتشين", "selectMnemonicLanguage": "اختر لغة العبارة السرية", "selectNumberOfMnemonics": "اختر عدد الكلمات", - "clickOnTheWordsToArrangeThemInMnemonicOrder": "انقر على الكلمات لترتيبها بالترتيب الصحيح.", - "mnemonicInpitErrorPleaseReEnter": "خطأ في إدخال العبارة السرية، أعد الإدخال", + "setTransactionPassword": "تعيين كلمة مرور المعاملة", + "skipBackup": "تخطي النسخ الاحتياطي", + "strength": { + "medium": "متوسط", + "strong": "قوي", + "weak": "ضعيف" + }, + "systemFingerprintingHasBeenDisabled": "تم تعطيل بصمة النظام", + "theIdentityIsNotBackedUpYouCanOnlyExitAfterTheBackupIsCompleted": "لم يتم نسخ الهوية احتياطياً، يمكنك الخروج فقط بعد اكتمال النسخ الاحتياطي.", "theMnemonicsAreSeparatedBySpacesAnd_12_15_18_21_24_36bitMnemonicsInSiplifiedChineseTraditionalChineseAndEnglishAreSipported": "العبارات السرية مفصولة بمسافات، ويتم دعم 12/15/18/21/24/36 كلمة بالصينية المبسطة والتقليدية والإنجليزية", - "theMultiChainWalletUnderThisMnemonicWillBeRestored": "سيتم استعادة محفظة السلاسل المتعددة تحت هذه العبارة السرية", "theMultiChainWalletThatWillRestoreThisMnemonicPhrase": "سيتم استعادة محفظة السلاسل المتعددة تحت هذه العبارة السرية", - "inTheNextStepYouCanSeeTheMnemonicWordsUsed": "في الخطوة التالية، سترى كلمات العبارة السرية", + "theMultiChainWalletUnderThisMnemonicWillBeRestored": "سيتم استعادة محفظة السلاسل المتعددة تحت هذه العبارة السرية", + "theSystemFingerprintHasBeenBanned": "تم حظر بصمة النظام، يرجى إعادة تعيين بصمة النظام أولاً.", "thisIsYourMnemonicGetTheMnemonicToOwnTheWalletAssetPleaseWriteItDownOnPaperAndKeepItInASafePlace": "هذه هي العبارة السرية الخاصة بك. احصل على العبارة السرية لامتلاك أصول المحفظة! اكتبها على ورقة واحتفظ بها في مكان آمن.", "transactionPassword": "كلمة مرور المعاملة", - "setTransactionPassword": "تعيين كلمة مرور المعاملة", - "enterTheTransactionPassword": "أدخل كلمة مرور المعاملة", "transactionPasswordLoseTips": "ملاحظة: بمجرد تعيين كلمة مرور المعاملة بنجاح، لا يمكن استردادها إذا فقدت!", - "pleaseKeepInMindAfterSettingTheTransactionPassword": "يرجى تذكر كلمة مرور المعاملة بعد تعيينها", - "queryInBlockchainExplorer": "استعلام في مستكشف البلوكتشين", - "clickHereToQueryTheTransferProgressThroughTheBlockBrowser": "انقر هنا للتحقق من تفاصيل التحويل في مستكشف البلوكتشين.", - "fingerprintPayment": "الدفع بالبصمة", - "fingerprintUnlock": "فتح بالبصمة", + "twoStepSecret": { + "alreadySet": "كلمة مرور الأمان معينة بالفعل لهذا العنوان", + "broadcastSuccess": "تم البث بنجاح", + "confirmDesc": "أدخل كلمة مرور الأمان مرة أخرى للتأكيد", + "confirmPlaceholder": "تأكيد كلمة مرور الأمان", + "confirmTitle": "[MISSING:ar] 确认安全密码", + "feeInfo": "تعيين كلمة مرور الأمان يتطلب رسوم الشبكة", + "inputDesc": "قم بتعيين كلمة مرور أمان لحماية تحويلاتك", + "inputPlaceholder": "أدخل كلمة مرور الأمان", + "lengthHint": "يُنصح بـ 6 أحرف على الأقل", + "notMatch": "كلمات المرور غير متطابقة", + "notSet": "لم يتم تعيين كلمة مرور الأمان", + "setFailed": "فشل تعيين كلمة مرور الأمان، حاول مرة أخرى", + "setSuccess": "تم تعيين كلمة مرور الأمان بنجاح", + "setSuccessDesc": "ستحتاج إلى إدخال كلمة مرور الأمان لكل عملية تحويل", + "setTitle": "تعيين كلمة مرور الأمان", + "setup": "تعيين كلمة مرور الأمان", + "tooShort": "كلمة مرور الأمان يجب أن تكون 6 أحرف على الأقل", + "txConfirmed": "تم تأكيد المعاملة", + "waitingConfirm": "تم بث المعاملة، في انتظار التأكيد...", + "walletLockDesc": "افتح المحفظة للتأكيد", + "walletLockPlaceholder": "أدخل قفل المحفظة" + }, "verifyFingerprint": "التحقق من البصمة", - "pleaseVerifyTheFingerprint": "يرجى التحقق من البصمة", - "fingerprintVerificationFailed": "فشل التحقق من البصمة", - "fingerprintAuthenticationLogin": "تسجيل الدخول بالبصمة", - "confirmToTurnOffFingerprintPayment": "تأكيد إيقاف الدفع بالبصمة؟", - "pleaseGoToThePhoneSystemToRegisterFingerprints": "يرجى الذهاب إلى نظام الهاتف لتسجيل البصمات", - "systemFingerprintingHasBeenDisabled": "تم تعطيل بصمة النظام", - "theSystemFingerprintHasBeenBanned": "تم حظر بصمة النظام، يرجى إعادة تعيين بصمة النظام أولاً.", - "loginWithFingerprintVerification": "تسجيل الدخول بالبصمة", - "applicationLock": "قفل التطبيق", + "walletLock": { + "biometric": "استخدام البصمة", + "cancel": "إلغاء", + "error": "النمط غير صحيح، حاول مرة أخرى", + "unlockDesc": "ارسم نمط قفل المحفظة", + "unlockTitle": "فتح المحفظة", + "verifyTitle": "[MISSING:ar] 验证钱包", + "verifying": "جاري التحقق..." + }, "whenTurnesOnYouCanUseYourFingerprintInsteadOfPassword": "عند التشغيل، سيتم تفعيل قفل البصمة", - "theIdentityIsNotBackedUpYouCanOnlyExitAfterTheBackupIsCompleted": "لم يتم نسخ الهوية احتياطياً، يمكنك الخروج فقط بعد اكتمال النسخ الاحتياطي.", - "pleaseBackupYourWalletNow": "يرجى نسخ محفظتك احتياطياً الآن", - "backupYourMnemonicNow": "نسخ العبارة السرية احتياطياً الآن", - "exportMnemonic": "تصدير العبارة السرية", - "strength": { - "medium": "متوسط", - "strong": "قوي", - "weak": "ضعيف" - } + "yourMnemonic": "العبارة السرية الخاصة بك" } diff --git a/src/i18n/locales/en/authorize.json b/src/i18n/locales/en/authorize.json index 029e2f5a..3caa8077 100644 --- a/src/i18n/locales/en/authorize.json +++ b/src/i18n/locales/en/authorize.json @@ -55,5 +55,8 @@ "authFailed": "Authorization failed", "patternIncorrect": "Incorrect pattern", "timeout": "Request timed out" + }, + "passwordConfirm": { + "title": "Confirm Password" } } diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index ad95f468..a3df0223 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -1,26 +1,29 @@ { - "transfer": "Transfer", - "receive": "Receive", - "paste": "Paste", - "addressPlaceholder": "Enter or paste address", - "noAssets": "No assets", - "balance": "Balance", - "exceedsBalance": "Exceeds available balance", - "copiedToClipboard": "Copied to clipboard", - "passwordStrength": "Password strength", "a11y": { + "addContact": "Add contact", + "appInfo": "App info", + "back": "Back", "chainSelector": "Select blockchain network", + "close": "Close", "closeDialog": "Close dialog", "copyAddress": "Copy address", + "editFee": "Edit fee", "hidePassword": "Hide password", + "highFeeWarning": "High fee warning", "invalidAddress": "Invalid address format", + "loading": "Loading", + "loadingFee": "Loading fee", + "moreActions": "More actions", + "notification": "Notification", "passwordStrength": "Password strength: {{strength}}", "paste": "Paste", + "permissions": "Permissions", "refresh": "Refresh", "scan": "Scan QR code", "scanQrCode": "Scan QR code", "selectChain": "Select chain", "selectPeriod": "Select time period", + "selectWallet": "Select wallet", "showPassword": "Show password", "skipToMain": "Skip to main content", "tabHistory": "Transaction History", @@ -29,37 +32,8 @@ "tabTransfer": "Transfer", "tabWallet": "Wallet", "tokenDetails": "View {{token}} details", - "back": "Back", - "close": "Close", - "loading": "Loading", - "addContact": "Add contact", - "moreActions": "More actions", - "notification": "Notification", - "loadingFee": "Loading fee", - "highFeeWarning": "High fee warning", - "editFee": "Edit fee", - "appInfo": "App info", - "unknownApp": "Unknown app", - "permissions": "Permissions", "transactionDetails": "Transaction details", - "selectWallet": "Select wallet" - }, - "contact": { - "addTitle": "Add Contact", - "editTitle": "Edit Contact", - "name": "Name", - "namePlaceholder": "Enter contact name", - "address": "Address", - "addressPlaceholder": "Enter wallet address", - "memoOptional": "Memo (optional)", - "memoPlaceholder": "Add memo", - "add": "Add", - "save": "Save", - "cancel": "Cancel", - "noContacts": "No contacts", - "viewAll": "View all contacts", - "selectContact": "Select Contact", - "selectAddress": "Select address" + "unknownApp": "Unknown app" }, "acceptExchange": "Accept exchange", "account": "[MISSING:en] 用户", @@ -67,6 +41,19 @@ "accountWorks": "[MISSING:en] account works!", "acquisitionTime": "Acquisition time", "add": "Add", + "addressBook": { + "delete": "Delete", + "deleteConfirm": "Are you sure you want to delete contact \"{{name}}\"?", + "deleteTitle": "Delete Contact", + "edit": "Edit", + "noContacts": "No contacts yet", + "noResults": "No contacts found", + "passwordError": "Incorrect password", + "searchPlaceholder": "Search contacts", + "title": "Address Book", + "verifyFailed": "Verification failed" + }, + "addressPlaceholder": "Enter or paste address", "advancedEncryptionTechnologyDigitalWealthIsMoreSecure": "Advanced encryption technology <br /> Digital wealth is more secure", "afterConfirmation_{appName}WillDeleteAllLocalDataTips": "After confirmation, will delete all local data and all wallets will be removed locally. Are you sure to exit?", "album": "Album", @@ -79,6 +66,7 @@ "appVersion": "V", "authorizationDoesNoiShareYourPrivateKeyInformation": "Authorization does not share your private key information", "back": "Back", + "balance": "Balance", "bandwidth": "Bandwidth", "beSureToKeepThePrivateKeyInASafePlaceIfThePrivateKeyIsLostItCannotBeRetrieved": "Be sure to keep the private key in a safe place. If the private key is lost,it cannot be retrieved", "belongingAlbum": "Belonging album", @@ -99,10 +87,28 @@ "confirmToDelete": "Confirm to delete ?", "confirmToTurnOffTouchId": "Confirm to turn off Touch ID?", "confirmToTurnOffVerification": "[MISSING:en] 确认要关闭验证吗?", + "contact": { + "add": "Add", + "addTitle": "Add Contact", + "address": "Address", + "addressPlaceholder": "Enter wallet address", + "cancel": "Cancel", + "editTitle": "Edit Contact", + "memoOptional": "Memo (optional)", + "memoPlaceholder": "Add memo", + "name": "Name", + "namePlaceholder": "Enter contact name", + "noContacts": "No contacts", + "save": "Save", + "selectAddress": "Select address", + "selectContact": "Select Contact", + "viewAll": "View all contacts" + }, "continue": "Continue", "contractMethods": "Contract methods", "contractRiskWarning": "The contract carries a certain level of risk. Users are requested to confirm the security of the contract code themselves. If authorized, the risk will be confirmed", "copied": "Copied", + "copiedToClipboard": "Copied to clipboard", "copy": "Copy", "copyPrivateKey": "Copy Private key", "copySuccess": "Copied successfully", @@ -131,8 +137,10 @@ "event": "Event", "eventContent": "Event Content", "eventSource": "Event Source", + "exceedsBalance": "Exceeds available balance", "exit": "Exit", "fastest": "Fastest", + "favorite": "Favorite", "fillableMemo": "Fillable Memo", "finish": "[MISSING:en] 完成", "firstTimeToUse": "First time to use", @@ -175,8 +183,7 @@ "networkNotConnected": "Network not connected", "newFinish": "Finish", "next": "Next", - "favorite": "Favorite", - "unfavorite": "Unfavorite", + "noAssets": "No assets", "noData": "No data", "noPossibleUtxoObtained": "No possible utxo obtained", "noResultsFound": "No results found", @@ -188,6 +195,8 @@ "openNewVersion": "Open new version", "out": "Out", "pageFooter": "Page footer", + "passwordStrength": "Password strength", + "paste": "Paste", "pay": "Pay", "payer": "Payer", "pending": "Pending", @@ -207,6 +216,7 @@ "project": "Project", "publishDate": "Publish Date", "quantity": "[MISSING:en] 数量", + "receive": "Receive", "receivingGifts": "Receiving gifts", "refuse": "Refuse", "reject": "Reject", @@ -259,14 +269,27 @@ "theRemarksCannotContainerSpaces": "The remarks cannot container spaces !", "thereAreCurrentlyNoSearchResultsAvailable": "There are currently no search results available", "thisIsTheLatestVersion": "This is the latest version", + "time": { + "daysAgo": "{{count}} days ago", + "daysLater": "In {{count}} days", + "hoursAgo": "{{count}} hr ago", + "hoursLater": "In {{count}} hr", + "justNow": "Just now", + "minutesAgo": "{{count}} min ago", + "minutesLater": "In {{count}} min", + "today": "Today", + "yesterday": "Yesterday" + }, "timeOut": "Time out", "toBePosted": "To be posted", "totalSupply": "Total Supply", "touchId": "Touch ID", + "transfer": "Transfer", "tryAgain": "Try again", "tryLater": "Try later", "turnOn": "Turn on", "twoInputsdoNotMatch": "Two inputs don't match!", + "unfavorite": "Unfavorite", "unknown": "Unknown", "unregistered": "Unregistered", "update": "Update", @@ -275,6 +298,7 @@ "userAgreement": "User agreement", "userAvatorAndNikename": "User avatar and nickname", "validationSucceeded": "Validation succeeded", + "verifying": "[MISSING:en] 验证中...", "version": "Version", "versionUpgrade": "version upgrade", "viewInBrowser": "View in browser", @@ -287,28 +311,5 @@ "{{item}}Words": "words", "{{length}}Words": "words", "中文(简体)": "中文(简体)", - "中文(繁體)": "中文(繁體)", - "addressBook": { - "title": "Address Book", - "searchPlaceholder": "Search contacts", - "noContacts": "No contacts yet", - "noResults": "No contacts found", - "edit": "Edit", - "delete": "Delete", - "deleteTitle": "Delete Contact", - "deleteConfirm": "Are you sure you want to delete contact \"{{name}}\"?", - "passwordError": "Incorrect password", - "verifyFailed": "Verification failed" - }, - "time": { - "justNow": "Just now", - "minutesAgo": "{{count}} min ago", - "hoursAgo": "{{count}} hr ago", - "daysAgo": "{{count}} days ago", - "minutesLater": "In {{count}} min", - "hoursLater": "In {{count}} hr", - "daysLater": "In {{count}} days", - "today": "Today", - "yesterday": "Yesterday" - } + "中文(繁體)": "中文(繁體)" } diff --git a/src/i18n/locales/en/security.json b/src/i18n/locales/en/security.json index 61fab2e0..466db8c4 100644 --- a/src/i18n/locales/en/security.json +++ b/src/i18n/locales/en/security.json @@ -1,141 +1,147 @@ { - "patternLock": { - "gridLabel": "{{size}}x{{size}} Pattern Lock", - "nodeLabel": "Node at row {{row}}, column {{col}}{{order, number}}", - "hint": "Connect at least {{min}} dots", - "needMore": "Connected {{current}} dots, need {{min, number}} more", - "error": "Pattern doesn't match, please try again", - "success": "Pattern correct", - "valid": "Connected {{count}} dots", - "clear": "Clear pattern", - "reset": "Reset", - "setTitle": "Set Wallet Lock", - "setDesc": "Draw a pattern as your wallet lock to protect your wallet", - "confirmTitle": "Confirm Wallet Lock", - "confirmDesc": "Draw the pattern again to confirm", - "unlockTitle": "Unlock Wallet", - "unlockDesc": "Draw your wallet lock pattern", - "mismatch": "Patterns don't match, please try again" - }, + "applicationLock": "Application lock", + "areYouSureYouWantToSkipBackup": "Are you sure you want to skip backup?", + "backupMnemonic": "Backup mnemonic", + "backupNow": "Backup Now", "backupTips": { - "title": "Backup Mnemonic", "description": "Mnemonic is the only way to recover your wallet, please keep it safe", + "proceed": "I Understand, Start Backup", + "skip": "Backup Later", "tips": { - "privacy": { - "title": "Ensure Privacy", - "description": "View mnemonic in a private environment to prevent others from peeking" - }, "noScreenshot": { - "title": "No Screenshots", - "description": "Do not save mnemonic electronically, write it down instead" + "description": "Do not save mnemonic electronically, write it down instead", + "title": "No Screenshots" + }, + "privacy": { + "description": "View mnemonic in a private environment to prevent others from peeking", + "title": "Ensure Privacy" }, "safekeeping": { - "title": "Safe Storage", - "description": "Keep mnemonic in a safe place, loss will result in unrecoverable assets" + "description": "Keep mnemonic in a safe place, loss will result in unrecoverable assets", + "title": "Safe Storage" } }, - "proceed": "I Understand, Start Backup", - "skip": "Backup Later" - }, - "walletLock": { - "unlockTitle": "Unlock Wallet", - "unlockDesc": "Draw your wallet lock pattern", - "verifying": "Verifying...", - "error": "Incorrect pattern, please try again", - "biometric": "Use Biometrics", - "cancel": "Cancel" - }, - "twoStepSecret": { - "setTitle": "Set Security Password", - "tooShort": "Security password must be at least 6 characters", - "lengthHint": "Recommend at least 6 chars", - "notMatch": "Passwords do not match", - "setFailed": "Failed to set security password, please try again", - "setSuccess": "Security Password Set Successfully", - "setSuccessDesc": "You will need to enter your security password for each transfer", - "broadcastSuccess": "Broadcast Successful", - "waitingConfirm": "Transaction broadcasted, waiting for confirmation...", - "txConfirmed": "Transaction Confirmed", - "feeInfo": "Setting security password requires a network fee", - "inputDesc": "Set a security password to protect your transfers", - "inputPlaceholder": "Enter security password", - "confirmDesc": "Enter security password again to confirm", - "confirmPlaceholder": "Confirm security password", - "walletLockDesc": "Unlock wallet to confirm", - "walletLockPlaceholder": "Enter wallet lock", - "alreadySet": "Security password is already set for this address", - "notSet": "Security password not set", - "setup": "Set Security Password" + "title": "Backup Mnemonic" }, + "backupYourMnemonicNow": "Backup Your Mnemonic Now", + "clickHereToQueryTheTransferProgressThroughTheBlockBrowser": "Click here to check transfer details in blockchain explorer.", + "clickOnTheWordsToArrangeThemInMnemonicOrder": "Click on the words to arrange them in mnemonic order.", + "confirmMnemonic": "Confirm Mnemonic", + "confirmToTurnOffFingerprintPayment": "Confirm to turn off Fingerprint payment?", + "copyMnemonic": "Copy mnemonic", + "doNotDiscloseOrShareTheMnemonicPhraseWithOthers": "Do not disclose or share the mnemonic phrase with others.", + "enterTheMnemonicSeparatedBySpaces": "Enter the mnemonic, separated by spaces", + "enterTheTransactionPassword": "Enter the transaction password", + "exportMnemonic": "Export Mnemonic", + "fingerprintAuthenticationLogin": "Login with Fingerprint Verification", + "fingerprintPayment": "Fingerprint payment", + "fingerprintUnlock": "Fingerprint unlock", + "fingerprintVerificationFailed": "Fingerprint verification failed", + "giveUpBackup": "Give up backup", + "ifYouLoseYourMnemonicPhraseYourAssetsAreLostForever": "If you lose your mnemonic phrase, your assets are lost forever.", + "inTheNextStepYouCanSeeTheMnemonicWordsUsed": "In the next step, you will see the mnemonic words", + "loginWithFingerprintVerification": "Login with Fingerprint Verification", + "mnemonic": "Mnemonic", "mnemonicConfirm": { - "selected": "Selected {{current}}/{{total}}", - "undo": "Undo", - "hint": "Click words below in correct order", + "clearAll": "Clear All", "error": "Mnemonic order incorrect, please try again", - "success": "Mnemonic verified successfully", + "hint": "Click words below in correct order", "reset": "Reset", - "clearAll": "Clear All" - }, - "mnemonicInput": { - "entered": "Entered {{filled}}/{{total}} words", - "clear": "Clear" + "selected": "Selected {{current}}/{{total}}", + "success": "Mnemonic verified successfully", + "undo": "Undo" }, + "mnemonicCopied": "Mnemonic copied", "mnemonicDisplay": { - "hidden": "••••••", "copied": "Copied", - "copy": "Copy Mnemonic" + "copy": "Copy Mnemonic", + "hidden": "••••••" }, - "mnemonic": "Mnemonic", - "confirmMnemonic": "Confirm Mnemonic", - "copyMnemonic": "Copy mnemonic", - "backupMnemonic": "Backup mnemonic", - "backupNow": "Backup Now", - "noBackup": "No Backup", - "skipBackup": "Skip backup", - "giveUpBackup": "Give up backup", - "areYouSureYouWantToSkipBackup": "Are you sure you want to skip backup?", "mnemonicImport": "Mnemonic import", - "mnemonicCopied": "Mnemonic copied", - "yourMnemonic": "Your Mnemonic", - "doNotDiscloseOrShareTheMnemonicPhraseWithOthers": "Do not disclose or share the mnemonic phrase with others.", - "ifYouLoseYourMnemonicPhraseYourAssetsAreLostForever": "If you lose your mnemonic phrase, your assets are lost forever.", - "enterTheMnemonicSeparatedBySpaces": "Enter the mnemonic, separated by spaces", + "mnemonicInpitErrorPleaseReEnter": "Mnemonic input error, please re-enter", + "mnemonicInput": { + "clear": "Clear", + "entered": "Entered {{filled}}/{{total}} words" + }, + "noBackup": "No Backup", + "password": "[MISSING:en] 密码", + "passwordConfirm": { + "verifying": "[MISSING:en] 验证中..." + }, + "patternLock": { + "clear": "Clear pattern", + "confirmDesc": "Draw the pattern again to confirm", + "confirmTitle": "Confirm Wallet Lock", + "error": "Pattern doesn't match, please try again", + "gridLabel": "{{size}}x{{size}} Pattern Lock", + "hint": "Connect at least {{min}} dots", + "mismatch": "Patterns don't match, please try again", + "needMore": "Connected {{current}} dots, need {{min, number}} more", + "nodeLabel": "Node at row {{row}}, column {{col}}{{order, number}}", + "reset": "Reset", + "setDesc": "Draw a pattern as your wallet lock to protect your wallet", + "setTitle": "Set Wallet Lock", + "success": "Pattern correct", + "unlockDesc": "Draw your wallet lock pattern", + "unlockTitle": "Unlock Wallet", + "valid": "Connected {{count}} dots" + }, + "pleaseBackupYourWalletNow": "Please backup your wallet now", + "pleaseGoToThePhoneSystemToRegisterFingerprints": "Please go to the phone system to register fingerprints", + "pleaseKeepInMindAfterSettingTheTransactionPassword": "Please keep in mind after setting the transaction password", + "pleaseVerifyTheFingerprint": "Please verify the fingerprint", + "queryInBlockchainExplorer": "Query In Blockchain Explorer", "selectMnemonicLanguage": "Select mnemonic language", "selectNumberOfMnemonics": "Select number of mnemonics", - "clickOnTheWordsToArrangeThemInMnemonicOrder": "Click on the words to arrange them in mnemonic order.", - "mnemonicInpitErrorPleaseReEnter": "Mnemonic input error, please re-enter", + "setTransactionPassword": "Set Transaction Password", + "skipBackup": "Skip backup", + "strength": { + "medium": "Medium", + "strong": "Strong", + "weak": "Weak" + }, + "systemFingerprintingHasBeenDisabled": "System fingerprinting has been disabled", + "theIdentityIsNotBackedUpYouCanOnlyExitAfterTheBackupIsCompleted": "The identity is not backed up, you can only exit after the backup is completed.", "theMnemonicsAreSeparatedBySpacesAnd_12_15_18_21_24_36bitMnemonicsInSiplifiedChineseTraditionalChineseAndEnglishAreSipported": "The mnemonics are separated by spaces, and 12/15/18/21/24/36-bit mnemonics in Simplified Chinese, Traditional Chinese, and English are supported. Just enter the passwords of Bioforest Chain directly", - "theMultiChainWalletUnderThisMnemonicWillBeRestored": "The multi-chain wallet under this mnemonic will be restored", "theMultiChainWalletThatWillRestoreThisMnemonicPhrase": "Will restore the multi-chain wallet under this mnemonic phrase", - "inTheNextStepYouCanSeeTheMnemonicWordsUsed": "In the next step, you will see the mnemonic words", + "theMultiChainWalletUnderThisMnemonicWillBeRestored": "The multi-chain wallet under this mnemonic will be restored", + "theSystemFingerprintHasBeenBanned": "The system fingerprint has been banned, please reset the system fingerprint first.", "thisIsYourMnemonicGetTheMnemonicToOwnTheWalletAssetPleaseWriteItDownOnPaperAndKeepItInASafePlace": "This is your mnemonic. Get the mnemonic to own the wallet asset! Please write it down on paper and keep it in a safe place.", "transactionPassword": "Transaction password", - "setTransactionPassword": "Set Transaction Password", - "enterTheTransactionPassword": "Enter the transaction password", "transactionPasswordLoseTips": "Note: Once the transaction password is successfully set, it cannot be retrieved if it is lost!", - "pleaseKeepInMindAfterSettingTheTransactionPassword": "Please keep in mind after setting the transaction password", - "queryInBlockchainExplorer": "Query In Blockchain Explorer", - "clickHereToQueryTheTransferProgressThroughTheBlockBrowser": "Click here to check transfer details in blockchain explorer.", - "fingerprintPayment": "Fingerprint payment", - "fingerprintUnlock": "Fingerprint unlock", + "twoStepSecret": { + "alreadySet": "Security password is already set for this address", + "broadcastSuccess": "Broadcast Successful", + "confirmDesc": "Enter security password again to confirm", + "confirmPlaceholder": "Confirm security password", + "confirmTitle": "[MISSING:en] 确认安全密码", + "feeInfo": "Setting security password requires a network fee", + "inputDesc": "Set a security password to protect your transfers", + "inputPlaceholder": "Enter security password", + "lengthHint": "Recommend at least 6 chars", + "notMatch": "Passwords do not match", + "notSet": "Security password not set", + "setFailed": "Failed to set security password, please try again", + "setSuccess": "Security Password Set Successfully", + "setSuccessDesc": "You will need to enter your security password for each transfer", + "setTitle": "Set Security Password", + "setup": "Set Security Password", + "tooShort": "Security password must be at least 6 characters", + "txConfirmed": "Transaction Confirmed", + "waitingConfirm": "Transaction broadcasted, waiting for confirmation...", + "walletLockDesc": "Unlock wallet to confirm", + "walletLockPlaceholder": "Enter wallet lock" + }, "verifyFingerprint": "Verify Fingerprint", - "pleaseVerifyTheFingerprint": "Please verify the fingerprint", - "fingerprintVerificationFailed": "Fingerprint verification failed", - "fingerprintAuthenticationLogin": "Login with Fingerprint Verification", - "confirmToTurnOffFingerprintPayment": "Confirm to turn off Fingerprint payment?", - "pleaseGoToThePhoneSystemToRegisterFingerprints": "Please go to the phone system to register fingerprints", - "systemFingerprintingHasBeenDisabled": "System fingerprinting has been disabled", - "theSystemFingerprintHasBeenBanned": "The system fingerprint has been banned, please reset the system fingerprint first.", - "loginWithFingerprintVerification": "Login with Fingerprint Verification", - "applicationLock": "Application lock", + "walletLock": { + "biometric": "Use Biometrics", + "cancel": "Cancel", + "error": "Incorrect pattern, please try again", + "unlockDesc": "Draw your wallet lock pattern", + "unlockTitle": "Unlock Wallet", + "verifyTitle": "[MISSING:en] 验证钱包", + "verifying": "Verifying..." + }, "whenTurnesOnYouCanUseYourFingerprintInsteadOfPassword": "When turned on, the fingerprint lock will be enabled", - "theIdentityIsNotBackedUpYouCanOnlyExitAfterTheBackupIsCompleted": "The identity is not backed up, you can only exit after the backup is completed.", - "pleaseBackupYourWalletNow": "Please backup your wallet now", - "backupYourMnemonicNow": "Backup Your Mnemonic Now", - "exportMnemonic": "Export Mnemonic", - "strength": { - "medium": "Medium", - "strong": "Strong", - "weak": "Weak" - } + "yourMnemonic": "Your Mnemonic" } diff --git a/src/i18n/locales/zh-CN/authorize.json b/src/i18n/locales/zh-CN/authorize.json index 4e595eff..d0b3886d 100644 --- a/src/i18n/locales/zh-CN/authorize.json +++ b/src/i18n/locales/zh-CN/authorize.json @@ -55,5 +55,8 @@ "authFailed": "授权失败", "patternIncorrect": "图案错误", "timeout": "请求已超时" + }, + "passwordConfirm": { + "title": "确认密码" } } diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 667383f4..3950ed17 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -1,4 +1,6 @@ { + "verifying": "验证中...", + "confirm": "确认", "transfer": "转账", "receive": "收款", "paste": "粘贴", diff --git a/src/i18n/locales/zh-CN/security.json b/src/i18n/locales/zh-CN/security.json index 6e5e19a4..c2df35db 100644 --- a/src/i18n/locales/zh-CN/security.json +++ b/src/i18n/locales/zh-CN/security.json @@ -1,4 +1,5 @@ { + "password": "密码", "patternLock": { "gridLabel": "{{size}}x{{size}} 图案锁", "nodeLabel": "第{{row}}行第{{col}}列节点{{order, number}}", @@ -40,12 +41,17 @@ "walletLock": { "unlockTitle": "解锁钱包", "unlockDesc": "请绘制钱包锁图案", + "verifyTitle": "验证钱包", "verifying": "验证中...", "error": "图案错误,请重试", "biometric": "使用生物识别", "cancel": "取消" }, + "passwordConfirm": { + "verifying": "验证中..." + }, "twoStepSecret": { + "confirmTitle": "确认安全密码", "setTitle": "设置安全密码", "tooShort": "安全密码至少6个字符", "lengthHint": "建议至少6位", diff --git a/src/i18n/locales/zh-TW/authorize.json b/src/i18n/locales/zh-TW/authorize.json index 0da7278c..4ab51aba 100644 --- a/src/i18n/locales/zh-TW/authorize.json +++ b/src/i18n/locales/zh-TW/authorize.json @@ -55,5 +55,8 @@ "authFailed": "授權失敗", "patternIncorrect": "圖案錯誤", "timeout": "請求已逾時" + }, + "passwordConfirm": { + "title": "Confirm Password" } } diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index bbc3c338..faf08aad 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -298,6 +298,7 @@ "userAgreement": "用戶協議", "userAvatorAndNikename": "用戶頭像和昵稱", "validationSucceeded": "驗證成功", + "verifying": "[MISSING:zh-TW] 验证中...", "version": "版本", "versionUpgrade": "版本更新", "viewInBrowser": "在瀏覽器中查看", diff --git a/src/i18n/locales/zh-TW/security.json b/src/i18n/locales/zh-TW/security.json index d12ff126..4ce10977 100644 --- a/src/i18n/locales/zh-TW/security.json +++ b/src/i18n/locales/zh-TW/security.json @@ -1,141 +1,147 @@ { - "patternLock": { - "gridLabel": "{{size}}x{{size}} 圖案鎖", - "nodeLabel": "第{{row}}行第{{col}}列節點{{order, number}}", - "hint": "請連接至少 {{min}} 個點", - "needMore": "已連接 {{current}} 個點,還需 {{min, number}} 個", - "error": "圖案不匹配,請重試", - "success": "圖案正確", - "valid": "已連接 {{count}} 個點", - "clear": "清除圖案", - "reset": "重新設定", - "setTitle": "設定錢包鎖", - "setDesc": "繪製圖案作為錢包鎖,用於保護您的錢包安全", - "confirmTitle": "確認錢包鎖", - "confirmDesc": "請再次繪製圖案以確認", - "unlockTitle": "解鎖錢包", - "unlockDesc": "請繪製錢包鎖圖案", - "mismatch": "兩次圖案不一致,請重新設定" - }, + "applicationLock": "應用鎖", + "areYouSureYouWantToSkipBackup": "確定要放棄備份嗎?", + "backupMnemonic": "備份助記詞", + "backupNow": "前往備份", "backupTips": { - "title": "備份助記詞", "description": "助記詞是恢復錢包的唯一方式,請務必妥善保管", + "proceed": "我已了解,開始備份", + "skip": "稍後備份", "tips": { - "privacy": { - "title": "確保周圍無人", - "description": "請在私密環境下查看助記詞,防止他人窺視" - }, "noScreenshot": { - "title": "禁止截圖或拍照", - "description": "不要以任何電子形式保存助記詞,建議手寫記錄" + "description": "不要以任何電子形式保存助記詞,建議手寫記錄", + "title": "禁止截圖或拍照" + }, + "privacy": { + "description": "請在私密環境下查看助記詞,防止他人窺視", + "title": "確保周圍無人" }, "safekeeping": { - "title": "妥善保管", - "description": "將助記詞保存在安全的地方,丟失將無法找回資產" + "description": "將助記詞保存在安全的地方,丟失將無法找回資產", + "title": "妥善保管" } }, - "proceed": "我已了解,開始備份", - "skip": "稍後備份" - }, - "walletLock": { - "unlockTitle": "解鎖錢包", - "unlockDesc": "請繪製錢包鎖圖案", - "verifying": "驗證中...", - "error": "圖案錯誤,請重試", - "biometric": "使用生物辨識", - "cancel": "取消" - }, - "twoStepSecret": { - "setTitle": "設置安全密碼", - "tooShort": "安全密碼至少6個字符", - "lengthHint": "建議至少6位", - "notMatch": "兩次輸入的密碼不一致", - "setFailed": "設置安全密碼失敗,請稍後重試", - "setSuccess": "安全密碼設置成功", - "setSuccessDesc": "之後每次轉賬都需要輸入安全密碼進行驗證", - "broadcastSuccess": "廣播成功", - "waitingConfirm": "交易已廣播,正在等待上鏈確認...", - "txConfirmed": "交易已上鏈", - "feeInfo": "設置安全密碼需要支付網路手續費", - "inputDesc": "請設置一個安全密碼,用於保護您的轉賬安全", - "inputPlaceholder": "請輸入安全密碼", - "confirmDesc": "請再次輸入安全密碼以確認", - "confirmPlaceholder": "請再次輸入安全密碼", - "walletLockDesc": "請解鎖錢包以確認設置", - "walletLockPlaceholder": "請輸入錢包鎖", - "alreadySet": "該地址已設置安全密碼", - "notSet": "未設置安全密碼", - "setup": "設置安全密碼" + "title": "備份助記詞" }, + "backupYourMnemonicNow": "立即備份助記詞", + "clickHereToQueryTheTransferProgressThroughTheBlockBrowser": "點擊此處,通過區塊鏈瀏覽器查看轉帳詳情。", + "clickOnTheWordsToArrangeThemInMnemonicOrder": "點擊單詞,按助記詞順序排列。", + "confirmMnemonic": "確認助記詞", + "confirmToTurnOffFingerprintPayment": "確定關閉指紋支付?", + "copyMnemonic": "複製助記詞", + "doNotDiscloseOrShareTheMnemonicPhraseWithOthers": "請勿向他人公開或分享助記詞。", + "enterTheMnemonicSeparatedBySpaces": "輸入助記詞,各單詞間以空格分隔", + "enterTheTransactionPassword": "請輸入交易密碼", + "exportMnemonic": "導出助記詞", + "fingerprintAuthenticationLogin": "使用指紋驗證登錄", + "fingerprintPayment": "指紋支付", + "fingerprintUnlock": "指紋鎖", + "fingerprintVerificationFailed": "指紋驗證失敗", + "giveUpBackup": "放棄備份", + "ifYouLoseYourMnemonicPhraseYourAssetsAreLostForever": "如果丟失了助記詞,您的資產將永遠丟失。", + "inTheNextStepYouCanSeeTheMnemonicWordsUsed": "接下來,您將會看到由12個字組成的助記詞", + "loginWithFingerprintVerification": "使用指紋驗證登錄", + "mnemonic": "助記詞", "mnemonicConfirm": { - "selected": "已選擇 {{current}}/{{total}}", - "undo": "撤銷", - "hint": "按正確順序點擊下方助記詞", + "clearAll": "清空", "error": "助記詞順序錯誤,請重試", - "success": "助記詞驗證成功", + "hint": "按正確順序點擊下方助記詞", "reset": "重新選擇", - "clearAll": "清空" - }, - "mnemonicInput": { - "entered": "已輸入 {{filled}}/{{total}} 個單詞", - "clear": "清除" + "selected": "已選擇 {{current}}/{{total}}", + "success": "助記詞驗證成功", + "undo": "撤銷" }, + "mnemonicCopied": "助記詞已複製", "mnemonicDisplay": { - "hidden": "••••••", "copied": "已複製", - "copy": "複製助記詞" + "copy": "複製助記詞", + "hidden": "••••••" }, - "mnemonic": "助記詞", - "confirmMnemonic": "確認助記詞", - "copyMnemonic": "複製助記詞", - "backupMnemonic": "備份助記詞", - "backupNow": "前往備份", - "noBackup": "未備份", - "skipBackup": "跳過備份", - "giveUpBackup": "放棄備份", - "areYouSureYouWantToSkipBackup": "確定要放棄備份嗎?", "mnemonicImport": "助記詞導入", - "mnemonicCopied": "助記詞已複製", - "yourMnemonic": "您的助記詞", - "doNotDiscloseOrShareTheMnemonicPhraseWithOthers": "請勿向他人公開或分享助記詞。", - "ifYouLoseYourMnemonicPhraseYourAssetsAreLostForever": "如果丟失了助記詞,您的資產將永遠丟失。", - "enterTheMnemonicSeparatedBySpaces": "輸入助記詞,各單詞間以空格分隔", + "mnemonicInpitErrorPleaseReEnter": "助記詞輸入錯誤,請重新輸入", + "mnemonicInput": { + "clear": "清除", + "entered": "已輸入 {{filled}}/{{total}} 個單詞" + }, + "noBackup": "未備份", + "password": "[MISSING:zh-TW] 密码", + "passwordConfirm": { + "verifying": "[MISSING:zh-TW] 验证中..." + }, + "patternLock": { + "clear": "清除圖案", + "confirmDesc": "請再次繪製圖案以確認", + "confirmTitle": "確認錢包鎖", + "error": "圖案不匹配,請重試", + "gridLabel": "{{size}}x{{size}} 圖案鎖", + "hint": "請連接至少 {{min}} 個點", + "mismatch": "兩次圖案不一致,請重新設定", + "needMore": "已連接 {{current}} 個點,還需 {{min, number}} 個", + "nodeLabel": "第{{row}}行第{{col}}列節點{{order, number}}", + "reset": "重新設定", + "setDesc": "繪製圖案作為錢包鎖,用於保護您的錢包安全", + "setTitle": "設定錢包鎖", + "success": "圖案正確", + "unlockDesc": "請繪製錢包鎖圖案", + "unlockTitle": "解鎖錢包", + "valid": "已連接 {{count}} 個點" + }, + "pleaseBackupYourWalletNow": "請立即備份錢包", + "pleaseGoToThePhoneSystemToRegisterFingerprints": "請先前往手機系統中錄入指紋", + "pleaseKeepInMindAfterSettingTheTransactionPassword": "設置交易密碼後請牢記", + "pleaseVerifyTheFingerprint": "請驗證指紋", + "queryInBlockchainExplorer": "在區塊鏈瀏覽器中查詢", "selectMnemonicLanguage": "選擇助記詞語言", "selectNumberOfMnemonics": "選擇助記詞數量", - "clickOnTheWordsToArrangeThemInMnemonicOrder": "點擊單詞,按助記詞順序排列。", - "mnemonicInpitErrorPleaseReEnter": "助記詞輸入錯誤,請重新輸入", + "setTransactionPassword": "設置交易密碼", + "skipBackup": "跳過備份", + "strength": { + "medium": "中等", + "strong": "強", + "weak": "弱" + }, + "systemFingerprintingHasBeenDisabled": "系統指紋已被禁止", + "theIdentityIsNotBackedUpYouCanOnlyExitAfterTheBackupIsCompleted": "當前身份未備份,須完成備份身份錢包助記詞才可退出。", "theMnemonicsAreSeparatedBySpacesAnd_12_15_18_21_24_36bitMnemonicsInSiplifiedChineseTraditionalChineseAndEnglishAreSipported": "助記詞之間用空格隔開,支持簡體中文、繁體中文、英文的 12/15/18/21/24/36 位助記詞導入。生物鏈密碼直接輸入即可。", - "theMultiChainWalletUnderThisMnemonicWillBeRestored": "將恢復此助記詞下的多鏈錢包", "theMultiChainWalletThatWillRestoreThisMnemonicPhrase": "將恢復此助記詞下的多鏈錢包", - "inTheNextStepYouCanSeeTheMnemonicWordsUsed": "接下來,您將會看到由12個字組成的助記詞", + "theMultiChainWalletUnderThisMnemonicWillBeRestored": "將恢復此助記詞下的多鏈錢包", + "theSystemFingerprintHasBeenBanned": "系統指紋已被禁止,請先重置系統指紋。", "thisIsYourMnemonicGetTheMnemonicToOwnTheWalletAssetPleaseWriteItDownOnPaperAndKeepItInASafePlace": "這是您的助記詞。獲得助記詞將擁有錢包資產所有權!請將其寫在紙上並保存在安全的地方。", "transactionPassword": "轉賬密碼", - "setTransactionPassword": "設置交易密碼", - "enterTheTransactionPassword": "請輸入交易密碼", "transactionPasswordLoseTips": "注意:交易密碼一旦設置成功,丟失將無法找回!", - "pleaseKeepInMindAfterSettingTheTransactionPassword": "設置交易密碼後請牢記", - "queryInBlockchainExplorer": "在區塊鏈瀏覽器中查詢", - "clickHereToQueryTheTransferProgressThroughTheBlockBrowser": "點擊此處,通過區塊鏈瀏覽器查看轉帳詳情。", - "fingerprintPayment": "指紋支付", - "fingerprintUnlock": "指紋鎖", + "twoStepSecret": { + "alreadySet": "該地址已設置安全密碼", + "broadcastSuccess": "廣播成功", + "confirmDesc": "請再次輸入安全密碼以確認", + "confirmPlaceholder": "請再次輸入安全密碼", + "confirmTitle": "[MISSING:zh-TW] 确认安全密码", + "feeInfo": "設置安全密碼需要支付網路手續費", + "inputDesc": "請設置一個安全密碼,用於保護您的轉賬安全", + "inputPlaceholder": "請輸入安全密碼", + "lengthHint": "建議至少6位", + "notMatch": "兩次輸入的密碼不一致", + "notSet": "未設置安全密碼", + "setFailed": "設置安全密碼失敗,請稍後重試", + "setSuccess": "安全密碼設置成功", + "setSuccessDesc": "之後每次轉賬都需要輸入安全密碼進行驗證", + "setTitle": "設置安全密碼", + "setup": "設置安全密碼", + "tooShort": "安全密碼至少6個字符", + "txConfirmed": "交易已上鏈", + "waitingConfirm": "交易已廣播,正在等待上鏈確認...", + "walletLockDesc": "請解鎖錢包以確認設置", + "walletLockPlaceholder": "請輸入錢包鎖" + }, "verifyFingerprint": "指紋驗證", - "pleaseVerifyTheFingerprint": "請驗證指紋", - "fingerprintVerificationFailed": "指紋驗證失敗", - "fingerprintAuthenticationLogin": "使用指紋驗證登錄", - "confirmToTurnOffFingerprintPayment": "確定關閉指紋支付?", - "pleaseGoToThePhoneSystemToRegisterFingerprints": "請先前往手機系統中錄入指紋", - "systemFingerprintingHasBeenDisabled": "系統指紋已被禁止", - "theSystemFingerprintHasBeenBanned": "系統指紋已被禁止,請先重置系統指紋。", - "loginWithFingerprintVerification": "使用指紋驗證登錄", - "applicationLock": "應用鎖", + "walletLock": { + "biometric": "使用生物辨識", + "cancel": "取消", + "error": "圖案錯誤,請重試", + "unlockDesc": "請繪製錢包鎖圖案", + "unlockTitle": "解鎖錢包", + "verifyTitle": "[MISSING:zh-TW] 验证钱包", + "verifying": "驗證中..." + }, "whenTurnesOnYouCanUseYourFingerprintInsteadOfPassword": "開啟後,將启用指紋鎖", - "theIdentityIsNotBackedUpYouCanOnlyExitAfterTheBackupIsCompleted": "當前身份未備份,須完成備份身份錢包助記詞才可退出。", - "pleaseBackupYourWalletNow": "請立即備份錢包", - "backupYourMnemonicNow": "立即備份助記詞", - "exportMnemonic": "導出助記詞", - "strength": { - "medium": "中等", - "strong": "強", - "weak": "弱" - } + "yourMnemonic": "您的助記詞" } diff --git a/src/pages/guide/WelcomeScreen.tsx b/src/pages/guide/WelcomeScreen.tsx index 1363fe00..99a08bf4 100644 --- a/src/pages/guide/WelcomeScreen.tsx +++ b/src/pages/guide/WelcomeScreen.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, useMemo } from 'react'; import { useNavigation } from '@/stackflow'; import { useTranslation } from 'react-i18next'; import { @@ -28,8 +28,8 @@ export function resetWelcome(): void { interface WelcomeSlide { icon: React.ReactNode; - titleKey: string; - descriptionKey: string; + title: string; + description: string; } export function WelcomeScreen() { @@ -38,23 +38,23 @@ export function WelcomeScreen() { const [currentSlide, setCurrentSlide] = useState(0); const migration = useMigrationOptional(); - const slides: WelcomeSlide[] = [ + const slides: WelcomeSlide[] = useMemo(() => [ { icon: , - titleKey: 'welcome.slides.transfer.title', - descriptionKey: 'welcome.slides.transfer.description', + title: t('welcome.slides.transfer.title'), + description: t('welcome.slides.transfer.description'), }, { icon: , - titleKey: 'welcome.slides.multichain.title', - descriptionKey: 'welcome.slides.multichain.description', + title: t('welcome.slides.multichain.title'), + description: t('welcome.slides.multichain.description'), }, { icon: , - titleKey: 'welcome.slides.security.title', - descriptionKey: 'welcome.slides.security.description', + title: t('welcome.slides.security.title'), + description: t('welcome.slides.security.description'), }, - ]; + ], [t]); const currentSlideData = slides[currentSlide]; if (!currentSlideData) return null; @@ -102,8 +102,8 @@ export function WelcomeScreen() {
{currentSlideData.icon}
-

{t(currentSlideData.titleKey)}

-

{t(currentSlideData.descriptionKey)}

+

{currentSlideData.title}

+

{currentSlideData.description}

{/* Dots indicator */}
diff --git a/src/pages/history/index.tsx b/src/pages/history/index.tsx index 63af2a14..0fc5e4fa 100644 --- a/src/pages/history/index.tsx +++ b/src/pages/history/index.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useNavigation } from '@/stackflow'; import { useTranslation } from 'react-i18next'; import { IconRefresh as RefreshCw, IconFilter as Filter } from '@tabler/icons-react'; @@ -11,14 +11,6 @@ import { cn } from '@/lib/utils'; import type { TransactionInfo } from '@/components/transaction/transaction-item'; import type { ChainType } from '@/stores'; -/** Period options - labels will be translated */ -const PERIOD_OPTIONS: { value: TransactionFilter['period']; labelKey: string }[] = [ - { value: 'all', labelKey: 'transaction:history.filter.allTime' }, - { value: '7d', labelKey: 'transaction:history.filter.days7' }, - { value: '30d', labelKey: 'transaction:history.filter.days30' }, - { value: '90d', labelKey: 'transaction:history.filter.days90' }, -]; - export function TransactionHistoryPage() { const { navigate, goBack } = useNavigation(); const currentWallet = useCurrentWallet(); @@ -27,13 +19,21 @@ export function TransactionHistoryPage() { const { t } = useTranslation(['transaction', 'common']); // 使用 TanStack Query 管理交易历史 const { transactions, isLoading, isFetching, filter, setFilter, refresh } = useTransactionHistoryQuery(currentWallet?.id); - const chainOptions: { value: ChainType | 'all'; labelKey?: string; label?: string }[] = [ - { value: 'all', labelKey: 'transaction:history.filter.allChains' }, + + const periodOptions = useMemo(() => [ + { value: 'all' as const, label: t('history.filter.allTime') }, + { value: '7d' as const, label: t('history.filter.days7') }, + { value: '30d' as const, label: t('history.filter.days30') }, + { value: '90d' as const, label: t('history.filter.days90') }, + ], [t]); + + const chainOptions = useMemo(() => [ + { value: 'all' as const, label: t('history.filter.allChains') }, ...enabledChains.map((chain) => ({ value: chain.id, label: chain.name, })), - ]; + ], [t, enabledChains]); // 初始化时设置默认过滤器为当前选中的网络 useEffect(() => { @@ -119,7 +119,7 @@ export function TransactionHistoryPage() { {chainOptions.map((option) => ( - {option.labelKey ? t(option.labelKey) : option.label} + {option.label} ))} @@ -134,9 +134,9 @@ export function TransactionHistoryPage() { - {PERIOD_OPTIONS.map((option) => ( - - {t(option.labelKey)} + {periodOptions.map((option) => ( + + {option.label} ))} diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index ad01969a..84c3f78b 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -56,7 +56,7 @@ export function HomePage() { const clipboard = useClipboard(); const toast = useToast(); const haptics = useHaptics(); - const { t } = useTranslation(); + const { t } = useTranslation(['home', 'common']); const isInitialized = useWalletInitialized(); const hasWallet = useHasWallet(); @@ -79,7 +79,7 @@ export function HomePage() { await clipboard.write({ text: chainAddress.address }); await haptics.impact('light'); setCopied(true); - toast.show(t('home:wallet.addressCopied')); + toast.show(t('wallet.addressCopied')); setTimeout(() => setCopied(false), 2000); } }; @@ -109,7 +109,7 @@ export function HomePage() { data-testid="chain-selector" onClick={handleOpenChainSelector} className="mb-4 flex items-center gap-2 rounded-full bg-white/20 px-3 py-1.5 text-sm text-white" - aria-label={t('a11y.chainSelector')} + aria-label={t('common:a11y.chainSelector')} > {selectedChainName} @@ -126,7 +126,7 @@ export function HomePage() { @@ -138,13 +138,13 @@ export function HomePage() {
navigate({ to: '/send' })}> - {t('home:wallet.send')} + {t('wallet.send')}
navigate({ to: '/receive' })}> - {t('home:wallet.receive')} + {t('wallet.receive')}
@@ -152,7 +152,7 @@ export function HomePage() { {/* 资产列表 */}
-

{t('home:wallet.assets')}

+

{t('wallet.assets')}

({ symbol: token.symbol, @@ -167,8 +167,8 @@ export function HomePage() { // TODO: Implement token detail page route once available console.log('Token clicked:', token.symbol); }} - emptyTitle={t('home:wallet.noAssets')} - emptyDescription={t('home:wallet.noAssetsOnChain', { chain: selectedChainName })} + emptyTitle={t('wallet.noAssets')} + emptyDescription={t('wallet.noAssetsOnChain', { chain: selectedChainName })} />
@@ -176,7 +176,7 @@ export function HomePage() { @@ -186,7 +186,7 @@ export function HomePage() { function NoWalletView() { const { navigate } = useNavigation(); - const { t } = useTranslation(); + const { t } = useTranslation(['home', 'common']); return (
@@ -194,15 +194,15 @@ function NoWalletView() {
-

{t('home:welcome.title')}

-

{t('home:welcome.subtitle')}

+

{t('welcome.title')}

+

{t('welcome.subtitle')}

navigate({ to: '/wallet/create' })}> - {t('home:welcome.createWallet')} + {t('welcome.createWallet')}
diff --git a/src/pages/notifications/index.tsx b/src/pages/notifications/index.tsx index 8768d814..585cd7b8 100644 --- a/src/pages/notifications/index.tsx +++ b/src/pages/notifications/index.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import type { TFunction } from 'i18next'; import { useNavigation } from '@/stackflow'; import { IconBell as Bell, IconCheck as Check, IconTrash as Trash2 } from '@tabler/icons-react'; import { useStore } from '@tanstack/react-store'; @@ -56,7 +57,7 @@ function NotificationItem({ onRead: (id: string) => void; onRemove: (id: string) => void; formatRelativeTime: (timestamp: number) => string; - t: (key: string) => string; + t: TFunction<'notification'>; }) { const style = typeStyles[notification.type]; @@ -117,7 +118,7 @@ function GroupedNotificationList({ onRead: (id: string) => void; onRemove: (id: string) => void; formatRelativeTime: (timestamp: number) => string; - t: (key: string) => string; + t: TFunction<'notification'>; language: string; }) { const grouped = useMemo(() => { diff --git a/src/pages/settings/chain-config.tsx b/src/pages/settings/chain-config.tsx index 166d17fd..75acfbaf 100644 --- a/src/pages/settings/chain-config.tsx +++ b/src/pages/settings/chain-config.tsx @@ -50,7 +50,7 @@ function getManualConfigIds(input: string): string[] { } } -function getSourceLabel(t: (key: string) => string, source: ChainConfigSource): string { +function getSourceLabel(t: (key: string, options?: object) => string, source: ChainConfigSource): string { switch (source) { case 'default': return t('chainConfig.source.default'); @@ -311,7 +311,7 @@ export function ChainConfigPage() { {configs.map((config, index) => { const warning = warningById.get(config.id); const disabledByWarning = warning?.kind === 'incompatible_major'; - const sourceLabel = getSourceLabel(t, config.source); + const sourceLabel = getSourceLabel(t as (key: string, options?: object) => string, config.source); return (
diff --git a/src/pages/settings/change-wallet-lock.test.tsx b/src/pages/settings/change-wallet-lock.test.tsx index 345b4453..456f572f 100644 --- a/src/pages/settings/change-wallet-lock.test.tsx +++ b/src/pages/settings/change-wallet-lock.test.tsx @@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event' import { ChangeWalletLockPage } from './change-wallet-lock' import { TestI18nProvider, testI18n } from '@/test/i18n-mock' -const t = testI18n.getFixedT('zh-CN') +const t = testI18n.getFixedT('zh-CN', ['settings', 'common', 'security']) // Mock stackflow const mockGoBack = vi.fn() diff --git a/src/pages/settings/index.test.tsx b/src/pages/settings/index.test.tsx index bb756ae3..3d08809f 100644 --- a/src/pages/settings/index.test.tsx +++ b/src/pages/settings/index.test.tsx @@ -7,7 +7,7 @@ import { SettingsPage } from './index'; import { TestI18nProvider, testI18n } from '@/test/i18n-mock'; import { IconWallet as Wallet } from '@tabler/icons-react'; -const t = testI18n.getFixedT('zh-CN'); +const t = testI18n.getFixedT('zh-CN', ['settings', 'common', 'security']); // Mock stackflow const mockNavigate = vi.fn(); diff --git a/src/pages/wallet/create.tsx b/src/pages/wallet/create.tsx index 80a8fee6..f53bbca8 100644 --- a/src/pages/wallet/create.tsx +++ b/src/pages/wallet/create.tsx @@ -30,7 +30,7 @@ const STEPS: Step[] = ['pattern', 'mnemonic', 'verify', 'chains']; export function WalletCreatePage() { const { navigate, goBack } = useNavigation(); - const { t } = useTranslation(); + const { t } = useTranslation('onboarding'); const chainConfigs = useChainConfigs(); const [step, setStep] = useState('pattern'); const [patternKey, setPatternKey] = useState(''); @@ -152,7 +152,7 @@ export function WalletCreatePage() { await walletActions.createWallet( { - name: t('onboarding:create.defaultWalletName'), + name: t('create.defaultWalletName'), keyType: 'mnemonic', address: primaryChain.address, chain: primaryChain.chain, @@ -164,14 +164,14 @@ export function WalletCreatePage() { navigate({ to: '/' }); } catch (error) { - console.error(t('onboarding:create.createFailed'), error); + console.error(t('create.createFailed'), error); setIsCreating(false); } }; return (
- + {/* 进度指示器 */}
@@ -215,7 +215,7 @@ export function WalletCreatePage() { selectionCount={selectedChainIds.length} onSelectionChange={setSelectedChainIds} onComplete={handleComplete} - completeLabel={t('onboarding:create.complete')} + completeLabel={t('create.complete')} isSubmitting={isCreating} />
@@ -235,25 +235,25 @@ interface MnemonicStepProps { } function MnemonicStep({ mnemonic, hidden, copied, onToggleHidden, onCopy, onContinue }: MnemonicStepProps) { - const { t } = useTranslation(); + const { t } = useTranslation('onboarding'); const canContinue = copied || !hidden; return (
-

{t('onboarding:create.backupMnemonic')}

-

{t('onboarding:create.backupHint')}

+

{t('create.backupMnemonic')}

+

{t('create.backupHint')}

- {t('onboarding:create.mnemonicWarning')} + {t('create.mnemonicWarning')}
- {t('onboarding:create.mnemonicTitle')} + {t('create.mnemonicTitle')}
@@ -261,7 +261,7 @@ function MnemonicStep({ mnemonic, hidden, copied, onToggleHidden, onCopy, onCont
- {canContinue ? t('onboarding:create.mnemonicBackedUp') : t('onboarding:create.mnemonicViewFirst')} + {canContinue ? t('create.mnemonicBackedUp') : t('create.mnemonicViewFirst')} {canContinue && }
@@ -274,7 +274,7 @@ interface VerifyStepProps { } function VerifyStep({ mnemonic, onContinue }: VerifyStepProps) { - const { t } = useTranslation(); + const { t } = useTranslation(['onboarding', 'common']); const [selectedIndices] = useState(() => { const indices = Array.from({ length: mnemonic.length }, (_, i) => i); const shuffled = indices.sort(() => Math.random() - 0.5); @@ -297,7 +297,7 @@ function VerifyStep({ mnemonic, onContinue }: VerifyStepProps) { const word = mnemonic[index]; const answer = answers[index]; if (answer && word && answer.toLowerCase() !== word.toLowerCase()) { - return t('onboarding:create.wordIncorrect'); + return t('create.wordIncorrect'); } return undefined; }; @@ -306,20 +306,20 @@ function VerifyStep({ mnemonic, onContinue }: VerifyStepProps) {
-

{t('onboarding:create.verifyTitle')}

-

{t('onboarding:create.verifyDesc')}

+

{t('create.verifyTitle')}

+

{t('create.verifyDesc')}

{selectedIndices.map((index) => ( - + handleInputChange(index, e.target.value)} className={cn(getFieldError(index) && 'border-destructive focus-visible:ring-destructive')} - placeholder={t('onboarding:create.wordNPlaceholder', { n: index + 1 })} + placeholder={t('create.wordNPlaceholder', { n: index + 1 })} autoCapitalize="off" autoCorrect="off" /> @@ -365,8 +365,8 @@ function ChainSelectionStep({
-

{t('onboarding:chainSelector.title')}

-

{t('onboarding:chainSelector.subtitle')}

+

{t('chainSelector.title')}

+

{t('chainSelector.subtitle')}

{chains.length === 0 ? ( diff --git a/src/pages/wallet/list.tsx b/src/pages/wallet/list.tsx index c142c895..82db41fe 100644 --- a/src/pages/wallet/list.tsx +++ b/src/pages/wallet/list.tsx @@ -76,7 +76,7 @@ export function WalletListPage() { isActive={wallet.id === currentWalletId} onSelect={() => handleSelectWallet(wallet.id)} onDetail={() => handleWalletDetail(wallet.id)} - t={t} + t={t as (key: string, options?: unknown) => string} /> ))}
diff --git a/src/stackflow/activities/sheets/TwoStepSecretConfirmJob.tsx b/src/stackflow/activities/sheets/TwoStepSecretConfirmJob.tsx index 18c7e07b..852e382c 100644 --- a/src/stackflow/activities/sheets/TwoStepSecretConfirmJob.tsx +++ b/src/stackflow/activities/sheets/TwoStepSecretConfirmJob.tsx @@ -33,7 +33,7 @@ type TwoStepSecretConfirmJobParams = { }; function TwoStepSecretConfirmJobContent() { - const { t } = useTranslation(["security", "transaction"]); + const { t } = useTranslation(["security", "transaction", "common"]); const { pop } = useFlow(); const { title, description } = useActivityParams(); diff --git a/src/stackflow/activities/tabs/WalletTab.tsx b/src/stackflow/activities/tabs/WalletTab.tsx index 5aab2fa4..f32321f4 100644 --- a/src/stackflow/activities/tabs/WalletTab.tsx +++ b/src/stackflow/activities/tabs/WalletTab.tsx @@ -9,7 +9,7 @@ import { cn } from "@/lib/utils"; export function WalletTab() { const { push } = useFlow(); - const { t } = useTranslation(); + const { t } = useTranslation(['wallet', 'common']); const wallets = useWallets(); const currentWallet = useCurrentWallet(); const currentWalletId = currentWallet?.id; @@ -25,7 +25,7 @@ export function WalletTab() { return (
- +
{/* Wallet List */} @@ -78,7 +78,7 @@ export function WalletTab() { type="button" onClick={(e) => handleWalletSettings(e, wallet.id, wallet.name)} className="shrink-0 rounded-full p-2 text-muted-foreground hover:bg-muted hover:text-foreground transition-colors" - aria-label={t('wallet:detail.title')} + aria-label={t('detail.title')} > @@ -93,8 +93,8 @@ export function WalletTab() {
-

{t('wallet:empty')}

-

{t('wallet:emptyHint')}

+

{t('empty')}

+

{t('emptyHint')}

)} @@ -106,7 +106,7 @@ export function WalletTab() { onClick={() => push("WalletAddJob", {})} > - {t('wallet:add')} + {t('add')}
diff --git a/src/stackflow/components/TabBar.tsx b/src/stackflow/components/TabBar.tsx index 48a4a030..250bd8d2 100644 --- a/src/stackflow/components/TabBar.tsx +++ b/src/stackflow/components/TabBar.tsx @@ -1,4 +1,5 @@ import { cn } from "@/lib/utils"; +import { useMemo } from "react"; import { IconHome, IconWallet, @@ -12,24 +13,24 @@ export type TabId = "home" | "wallet" | "transfer" | "settings"; interface Tab { id: TabId; - labelKey: string; + label: string; icon: Icon; } -const tabConfigs: Tab[] = [ - { id: "home", labelKey: "a11y.tabHome", icon: IconHome }, - { id: "wallet", labelKey: "a11y.tabWallet", icon: IconWallet }, - { id: "transfer", labelKey: "a11y.tabTransfer", icon: IconArrowsExchange }, - { id: "settings", labelKey: "a11y.tabSettings", icon: IconSettings }, -]; - interface TabBarProps { activeTab: TabId; onTabChange: (tab: TabId) => void; } export function TabBar({ activeTab, onTabChange }: TabBarProps) { - const { t } = useTranslation(); + const { t } = useTranslation('common'); + + const tabConfigs: Tab[] = useMemo(() => [ + { id: "home", label: t('a11y.tabHome'), icon: IconHome }, + { id: "wallet", label: t('a11y.tabWallet'), icon: IconWallet }, + { id: "transfer", label: t('a11y.tabTransfer'), icon: IconArrowsExchange }, + { id: "settings", label: t('a11y.tabSettings'), icon: IconSettings }, + ], [t]); return (
@@ -37,7 +38,7 @@ export function TabBar({ activeTab, onTabChange }: TabBarProps) { {tabConfigs.map((tab) => { const Icon = tab.icon; const isActive = activeTab === tab.id; - const label = t(tab.labelKey); + const label = tab.label; return (