Skip to content

Commit 539841f

Browse files
fix(i18n): 修复多语言支持和主题切换功能 (#14)
* feat(i18n): 添加设置页和创建钱包页的多语言支持 - 添加 settings.json 翻译 (sections, items, appearance) - 添加 onboarding.json 翻译 (create, recover) - 创建 AppearanceSheet 组件用于主题切换 - 重构 SettingsPage 使用 i18n - 更新 OnboardingCreatePage 使用 i18n Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(i18n): 添加交易记录和通知页面的多语言支持 - 添加 transaction.json 翻译 (history, detail, type, status) - 创建 notification.json 翻译文件 - 更新 TransactionHistoryPage 使用 i18n - 更新 TransactionDetailPage 使用 i18n - 更新 NotificationCenterPage 使用 i18n Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(i18n): 添加钱包列表页面的多语言支持 - 添加 wallet.json 翻译 (list section) - 更新 WalletListPage 使用 i18n Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(i18n): 更新恢复钱包页面使用多语言 - 更新 OnboardingRecoverPage 使用 i18n keys - 移除硬编码的中文字符串 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: 更新测试以适配 i18n 改动 - 添加 useTheme mock 到 settings.test.tsx - 更新 i18n-mock 添加 transaction, notification, wallet namespaces - 修复 notifications 测试中的 unread count 断言 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --------- Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1 parent afde4e0 commit 539841f

32 files changed

Lines changed: 779 additions & 153 deletions
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useTranslation } from 'react-i18next';
2+
import { IconSun, IconMoon, IconDeviceDesktop, IconCheck } from '@tabler/icons-react';
3+
import {
4+
Sheet,
5+
SheetContent,
6+
SheetHeader,
7+
SheetTitle,
8+
} from '@/components/ui/sheet';
9+
import { useTheme, preferencesActions } from '@/stores/preferences';
10+
import { cn } from '@/lib/utils';
11+
12+
interface AppearanceSheetProps {
13+
open: boolean;
14+
onOpenChange: (open: boolean) => void;
15+
}
16+
17+
type ThemeOption = 'system' | 'light' | 'dark';
18+
19+
const themeOptions: { value: ThemeOption; icon: typeof IconSun }[] = [
20+
{ value: 'system', icon: IconDeviceDesktop },
21+
{ value: 'light', icon: IconSun },
22+
{ value: 'dark', icon: IconMoon },
23+
];
24+
25+
export function AppearanceSheet({ open, onOpenChange }: AppearanceSheetProps) {
26+
const { t } = useTranslation('settings');
27+
const currentTheme = useTheme();
28+
29+
const handleThemeChange = (theme: ThemeOption) => {
30+
preferencesActions.setTheme(theme);
31+
onOpenChange(false);
32+
};
33+
34+
return (
35+
<Sheet open={open} onOpenChange={onOpenChange}>
36+
<SheetContent side="bottom" className="rounded-t-2xl">
37+
<SheetHeader>
38+
<SheetTitle>{t('appearance.title')}</SheetTitle>
39+
</SheetHeader>
40+
<div className="mt-4 space-y-2">
41+
{themeOptions.map(({ value, icon: Icon }) => (
42+
<button
43+
key={value}
44+
onClick={() => handleThemeChange(value)}
45+
className={cn(
46+
'flex w-full items-center gap-3 rounded-xl p-4 transition-colors',
47+
currentTheme === value
48+
? 'bg-primary/10 text-primary'
49+
: 'hover:bg-muted'
50+
)}
51+
>
52+
<Icon size={24} />
53+
<span className="flex-1 text-left font-medium">
54+
{t(`appearance.${value}`)}
55+
</span>
56+
{currentTheme === value && <IconCheck size={20} />}
57+
</button>
58+
))}
59+
</div>
60+
</SheetContent>
61+
</Sheet>
62+
);
63+
}

‎src/i18n/index.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import enMigration from './locales/en/migration.json'
1919
import enAuthorize from './locales/en/authorize.json'
2020
import enCurrency from './locales/en/currency.json'
2121
import enOnboarding from './locales/en/onboarding.json'
22+
import enNotification from './locales/en/notification.json'
2223

2324
// Namespace imports - zh-CN
2425
import zhCNCommon from './locales/zh-CN/common.json'
@@ -38,6 +39,7 @@ import zhCNMigration from './locales/zh-CN/migration.json'
3839
import zhCNAuthorize from './locales/zh-CN/authorize.json'
3940
import zhCNCurrency from './locales/zh-CN/currency.json'
4041
import zhCNOnboarding from './locales/zh-CN/onboarding.json'
42+
import zhCNNotification from './locales/zh-CN/notification.json'
4143

4244
// Namespace imports - zh-TW
4345
import zhTWCommon from './locales/zh-TW/common.json'
@@ -57,6 +59,7 @@ import zhTWMigration from './locales/zh-TW/migration.json'
5759
import zhTWAuthorize from './locales/zh-TW/authorize.json'
5860
import zhTWCurrency from './locales/zh-TW/currency.json'
5961
import zhTWOnboarding from './locales/zh-TW/onboarding.json'
62+
import zhTWNotification from './locales/zh-TW/notification.json'
6063

6164
// Namespace imports - ar
6265
import arCommon from './locales/ar/common.json'
@@ -76,6 +79,7 @@ import arMigration from './locales/ar/migration.json'
7679
import arAuthorize from './locales/ar/authorize.json'
7780
import arCurrency from './locales/ar/currency.json'
7881
import arOnboarding from './locales/ar/onboarding.json'
82+
import arNotification from './locales/ar/notification.json'
7983

8084
// 语言配置
8185
export const languages = {
@@ -108,6 +112,7 @@ export const namespaces = [
108112
'migration',
109113
'authorize',
110114
'currency',
115+
'notification',
111116
] as const
112117

113118
export type Namespace = (typeof namespaces)[number]
@@ -144,6 +149,7 @@ i18n.use(initReactI18next).init({
144149
migration: enMigration,
145150
authorize: enAuthorize,
146151
currency: enCurrency,
152+
notification: enNotification,
147153
},
148154
'zh-CN': {
149155
common: zhCNCommon,
@@ -163,6 +169,7 @@ i18n.use(initReactI18next).init({
163169
migration: zhCNMigration,
164170
authorize: zhCNAuthorize,
165171
currency: zhCNCurrency,
172+
notification: zhCNNotification,
166173
},
167174
'zh-TW': {
168175
common: zhTWCommon,
@@ -182,6 +189,7 @@ i18n.use(initReactI18next).init({
182189
migration: zhTWMigration,
183190
authorize: zhTWAuthorize,
184191
currency: zhTWCurrency,
192+
notification: zhTWNotification,
185193
},
186194
ar: {
187195
common: arCommon,
@@ -201,6 +209,7 @@ i18n.use(initReactI18next).init({
201209
migration: arMigration,
202210
authorize: arAuthorize,
203211
currency: arCurrency,
212+
notification: arNotification,
204213
},
205214
},
206215
lng: defaultLanguage,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"title": "مركز الإشعارات",
3+
"markAllRead": "تحديد الكل كمقروء",
4+
"unreadCount": "{{count}} غير مقروء",
5+
"noUnread": "لا توجد إشعارات غير مقروءة",
6+
"clear": "مسح",
7+
"delete": "حذف",
8+
"unread": "غير مقروء",
9+
"empty": {
10+
"title": "لا توجد إشعارات",
11+
"desc": "ستظهر جميع إشعاراتك هنا"
12+
},
13+
"time": {
14+
"justNow": "الآن",
15+
"minutesAgo": "منذ {{count}} دقيقة",
16+
"hoursAgo": "منذ {{count}} ساعة",
17+
"daysAgo": "منذ {{count}} يوم",
18+
"today": "اليوم",
19+
"yesterday": "أمس"
20+
}
21+
}

‎src/i18n/locales/ar/onboarding.json‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
11
{
2+
"create": {
3+
"title": "إنشاء محفظة",
4+
"backupMnemonic": "نسخ احتياطي للعبارة السرية",
5+
"verifyMnemonic": "التحقق من العبارة السرية",
6+
"backupHint": "يرجى كتابة الكلمات التالية بالترتيب والاحتفاظ بها في مكان آمن",
7+
"confirmWritten": "لقد كتبتها",
8+
"backupComplete": "اكتمل النسخ الاحتياطي!",
9+
"backupSuccessDesc": "لقد قمت بنسخ العبارة السرية احتياطيًا بنجاح. يرجى الاحتفاظ بها في مكان آمن.",
10+
"enterWallet": "دخول المحفظة"
11+
},
12+
"recover": {
13+
"title": "استعادة المحفظة",
14+
"addressConflict": "تعارض العنوان",
15+
"setPassword": "تعيين كلمة المرور",
16+
"passwordHint": "تُستخدم كلمة المرور لتشفير مفاتيح محفظتك. يرجى تذكرها.",
17+
"passwordRule": "8 أحرف على الأقل، بدون مسافات.",
18+
"password": "كلمة المرور",
19+
"confirmPassword": "تأكيد كلمة المرور",
20+
"passwordPlaceholder": "أدخل كلمة المرور",
21+
"confirmPlaceholder": "أدخل كلمة المرور مرة أخرى",
22+
"errors": {
23+
"minLength": "يجب أن تكون كلمة المرور 8 أحرف على الأقل",
24+
"noSpaces": "لا يمكن أن تحتوي كلمة المرور على مسافات",
25+
"mismatch": "كلمات المرور غير متطابقة",
26+
"mnemonicIncomplete": "بيانات العبارة السرية غير مكتملة"
27+
}
28+
},
229
"keyType": {
330
"title": "Select Key Type",
431
"mnemonic": "Standard Mnemonic (BIP39)",

‎src/i18n/locales/ar/settings.json‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
{
2-
"language": "language",
3-
"settings": "Settings",
2+
"language": "اللغة",
3+
"settings": "الإعدادات",
4+
"title": "الإعدادات",
5+
"sections": {
6+
"walletManagement": "إدارة المحفظة",
7+
"security": "الأمان",
8+
"preferences": "التفضيلات",
9+
"about": "حول"
10+
},
11+
"items": {
12+
"walletManagement": "إدارة المحفظة",
13+
"addressBook": "دفتر العناوين",
14+
"appLock": "قفل التطبيق",
15+
"viewMnemonic": "عرض العبارة السرية",
16+
"changePassword": "تغيير كلمة المرور",
17+
"language": "اللغة",
18+
"currency": "العملة",
19+
"chainConfig": "تهيئة السلاسل",
20+
"appearance": "المظهر",
21+
"aboutApp": "حول BFM Pay"
22+
},
23+
"appearance": {
24+
"title": "المظهر",
25+
"system": "اتباع النظام",
26+
"light": "الوضع الفاتح",
27+
"dark": "الوضع الداكن"
28+
},
29+
"chainAddressCount": "{{count}} عناوين سلاسل",
30+
"notSupported": "غير مدعوم",
431
"chainConfig": {
532
"title": "تهيئة السلاسل",
633
"subscription": {

‎src/i18n/locales/ar/transaction.json‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
11
{
2+
"history": {
3+
"title": "سجل المعاملات",
4+
"noWallet": "يرجى إنشاء أو استيراد محفظة أولاً",
5+
"totalRecords": "{{count}} سجل",
6+
"emptyTitle": "لا توجد معاملات",
7+
"emptyDesc": "لم يتم العثور على معاملات بالفلاتر الحالية",
8+
"filter": {
9+
"allChains": "جميع السلاسل",
10+
"allTime": "الكل",
11+
"days7": "7 أيام",
12+
"days30": "30 يومًا",
13+
"days90": "90 يومًا"
14+
}
15+
},
16+
"detail": {
17+
"title": "تفاصيل المعاملة",
18+
"notFound": "المعاملة غير موجودة أو منتهية الصلاحية",
19+
"info": "معلومات المعاملة",
20+
"toAddress": "عنوان المستلم",
21+
"fromAddress": "عنوان المرسل",
22+
"time": "الوقت",
23+
"network": "الشبكة",
24+
"fee": "الرسوم",
25+
"blockHeight": "ارتفاع الكتلة",
26+
"confirmations": "التأكيدات",
27+
"hash": "هاش المعاملة",
28+
"copied": "تم النسخ",
29+
"copyHash": "نسخ الهاش",
30+
"viewExplorer": "عرض في المستكشف"
31+
},
32+
"type": {
33+
"send": "إرسال",
34+
"receive": "استلام",
35+
"swap": "تبديل",
36+
"stake": "تخزين",
37+
"unstake": "إلغاء التخزين",
38+
"approve": "موافقة"
39+
},
40+
"status": {
41+
"confirmed": "مؤكد",
42+
"pending": "قيد الانتظار",
43+
"failed": "فشل"
44+
},
245
"aboutFee": "About Fee",
346
"amount": "Amount",
447
"amountShouldBe_{min}-{max}-{assettype}": "Amount should be -",

‎src/i18n/locales/ar/wallet.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
{
2+
"list": {
3+
"title": "إدارة المحافظ",
4+
"empty": "لا توجد محافظ بعد",
5+
"create": "إنشاء محفظة",
6+
"current": "الحالية",
7+
"chainAddresses": "{{count}} عناوين سلاسل",
8+
"viewDetail": "عرض التفاصيل"
9+
},
210
" obtainingTheAddressPassowrdIsEquivalentToOwningTheWalletAsset": "Obtaining the address password is equivalent to owning the wallet asset",
311
"1AllWalletDataInTheOriginal_{appName}": "1. All wallet data in the original will be retained and the data will be organized. The rules are as follows:",
412
"addBitcoinAddress": "Add Bitcoin address",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"title": "Notifications",
3+
"markAllRead": "Mark all read",
4+
"unreadCount": "{{count}} unread",
5+
"noUnread": "No unread notifications",
6+
"clear": "Clear",
7+
"delete": "Delete",
8+
"unread": "Unread",
9+
"empty": {
10+
"title": "No notifications",
11+
"desc": "All your notifications will appear here"
12+
},
13+
"time": {
14+
"justNow": "Just now",
15+
"minutesAgo": "{{count}} min ago",
16+
"hoursAgo": "{{count}} hours ago",
17+
"daysAgo": "{{count}} days ago",
18+
"today": "Today",
19+
"yesterday": "Yesterday"
20+
}
21+
}

‎src/i18n/locales/en/onboarding.json‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
11
{
2+
"create": {
3+
"title": "Create Wallet",
4+
"backupMnemonic": "Backup Mnemonic",
5+
"verifyMnemonic": "Verify Mnemonic",
6+
"backupHint": "Please write down the following mnemonic words in order and keep them safe",
7+
"confirmWritten": "I have written it down",
8+
"backupComplete": "Backup Complete!",
9+
"backupSuccessDesc": "You have successfully backed up your mnemonic. Please keep it safe.",
10+
"enterWallet": "Enter Wallet"
11+
},
12+
"recover": {
13+
"title": "Recover Wallet",
14+
"addressConflict": "Address Conflict",
15+
"setPassword": "Set Password",
16+
"passwordHint": "Password is used to encrypt your wallet keys. Please remember it.",
17+
"passwordRule": "At least 8 characters, no spaces allowed.",
18+
"password": "Password",
19+
"confirmPassword": "Confirm Password",
20+
"passwordPlaceholder": "Enter password",
21+
"confirmPlaceholder": "Enter password again",
22+
"errors": {
23+
"minLength": "Password must be at least 8 characters",
24+
"noSpaces": "Password cannot contain spaces",
25+
"mismatch": "Passwords do not match",
26+
"mnemonicIncomplete": "Mnemonic data is incomplete"
27+
}
28+
},
229
"keyType": {
330
"title": "Select Key Type",
431
"mnemonic": "Standard Mnemonic (BIP39)",

‎src/i18n/locales/en/settings.json‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
{
2-
"language": "language",
2+
"language": "Language",
33
"settings": "Settings",
4+
"title": "Settings",
5+
"sections": {
6+
"walletManagement": "Wallet Management",
7+
"security": "Security",
8+
"preferences": "Preferences",
9+
"about": "About"
10+
},
11+
"items": {
12+
"walletManagement": "Wallet Management",
13+
"addressBook": "Address Book",
14+
"appLock": "App Lock",
15+
"viewMnemonic": "View Mnemonic",
16+
"changePassword": "Change Password",
17+
"language": "Language",
18+
"currency": "Currency",
19+
"chainConfig": "Chain Config",
20+
"appearance": "Appearance",
21+
"aboutApp": "About BFM Pay"
22+
},
23+
"appearance": {
24+
"title": "Appearance",
25+
"system": "Follow System",
26+
"light": "Light Mode",
27+
"dark": "Dark Mode"
28+
},
29+
"chainAddressCount": "{{count}} chain addresses",
30+
"notSupported": "Not supported",
431
"chainConfig": {
532
"title": "Chain Config",
633
"subscription": {

0 commit comments

Comments
 (0)