feat(i18n): 添加马来语支持并重构多语言功能

This commit is contained in:
JiaJun
2026-04-21 14:25:50 +08:00
parent 052ade8bab
commit 236ae4b37e
7 changed files with 231 additions and 23 deletions

View File

@@ -1,7 +1,7 @@
import {useState} from 'react'
import {useMutation} from '@tanstack/react-query'
import {Languages} from 'lucide-react'
import {Check, Languages} from 'lucide-react'
import {useTranslation} from 'react-i18next'
import PageLayout from '@/components/layout'
@@ -33,7 +33,13 @@ import {claim} from '@/api/business.ts'
import {notifyError, notifySuccess} from '@/features/notifications'
import {MotionButton, MotionLink, tapMotionProps} from '@/lib/motion'
import {useUserStore} from "@/store/user.ts";
import {normalizeLanguage} from '@/lib/i18n'
import {type AppLanguage, normalizeLanguage} from '@/lib/i18n'
const LANGUAGE_OPTIONS: Array<{value: AppLanguage; labelKey: 'switchToChinese' | 'switchToEnglish' | 'switchToMalay'}> = [
{value: 'zh', labelKey: 'switchToChinese'},
{value: 'en', labelKey: 'switchToEnglish'},
{value: 'ms', labelKey: 'switchToMalay'},
]
function QuickNavCard({icon: Icon, label, to}: QuickNavCardProps) {
return (
@@ -65,6 +71,7 @@ function getProgressPercent(current = 0, total = 0) {
function HomePage() {
const {t} = useTranslation()
const [claimModalOpen, setClaimModalOpen] = useState(false)
const [languageModalOpen, setLanguageModalOpen] = useState(false)
const navigate = useNavigate()
const authInfo = useUserStore(state => state.authInfo)
const language = useUserStore((state) => state.language)
@@ -87,6 +94,7 @@ function HomePage() {
const {assetsInfo} = useAssetsQuery()
const claimProgress = getProgressPercent(assetsInfo?.today_claimed, assetsInfo?.today_limit)
const isClaimAvailable = (assetsInfo?.locked_points ?? 0) > 0
const currentLanguage = normalizeLanguage(language)
const previewCategories: ProductCategory[] = productCategories.map((category) => ({
...category,
items: category.items.slice(0, 4),
@@ -136,9 +144,9 @@ function HomePage() {
navigate(`/goods?type=${type}`)
}
const handleToggleLanguage = () => {
const currentLanguage = normalizeLanguage(language)
setLanguage(currentLanguage === 'zh' ? 'en' : 'zh')
const handleSelectLanguage = (nextLanguage: AppLanguage) => {
setLanguage(nextLanguage)
setLanguageModalOpen(false)
}
return (
@@ -150,7 +158,7 @@ function HomePage() {
<MotionButton
type="button"
className="liquid-glass-bg flex items-center justify-between gap-[12px] rounded-[12px] px-[12px] py-[8px] text-[13px] text-white/88 transition-colors hover:bg-white/28 focus-visible:ring-2 focus-visible:ring-[#FE9F00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070E]"
onClick={handleToggleLanguage}
onClick={() => setLanguageModalOpen(true)}
{...tapMotionProps}
>
<div className="flex min-w-0 items-center gap-[10px]">
@@ -159,7 +167,7 @@ function HomePage() {
<Languages className="h-[16px] w-[16px] shrink-0" aria-hidden="true"/>
</div>
<div
className="truncate font-medium">{normalizeLanguage(language) === 'zh' ? t('nav.switchToEnglish') : t('nav.switchToChinese')}</div>
className="truncate font-medium">{t(`nav.${LANGUAGE_OPTIONS.find((option) => option.value === currentLanguage)?.labelKey ?? 'switchToChinese'}`)}</div>
</div>
<ChevronRight className="h-[16px] w-[16px] shrink-0 text-white/70" aria-hidden="true"/>
</MotionButton>
@@ -303,6 +311,27 @@ function HomePage() {
onChangeAddressForm={redeem.changeAddressForm}
/>
<Modal
open={languageModalOpen}
title={t('common.language')}
onClose={() => setLanguageModalOpen(false)}
className="max-w-[420px]"
bodyClassName="space-y-3"
>
{LANGUAGE_OPTIONS.map((option) => (
<Button
key={option.value}
type="button"
variant={option.value === currentLanguage ? 'orange' : 'gray'}
className="flex h-[46px] w-full items-center justify-between px-[14px] text-[14px]"
onClick={() => handleSelectLanguage(option.value)}
>
<span>{t(`nav.${option.labelKey}`)}</span>
{option.value === currentLanguage ? <Check className="h-[16px] w-[16px] shrink-0"/> : null}
</Button>
))}
</Modal>
<Modal
open={claimModalOpen}
title={t('home.confirmClaim')}