45 個 component + 6 個 page 統一從舊 font-mono 遷移到 font-body (DM Mono),確保設計系統一致性。 font-body = DM Mono (等寬),視覺效果相同但走新設計 token。 保留: font-heading (Syne)、font-dot-matrix (VT323/DSEG7) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* LanguageSwitcher - 賽博風語系切換器
|
|
*
|
|
* Nothing.tech 風格極簡設計
|
|
* 支援 EN / 繁中 切換
|
|
*/
|
|
|
|
import { useLocale } from 'next-intl'
|
|
import { useRouter, usePathname, locales, localeShortNames, type Locale } from '@/i18n/routing'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface LanguageSwitcherProps {
|
|
className?: string
|
|
}
|
|
|
|
export function LanguageSwitcher({ className }: LanguageSwitcherProps) {
|
|
const locale = useLocale() as Locale
|
|
const router = useRouter()
|
|
const pathname = usePathname()
|
|
|
|
const handleSwitch = (newLocale: Locale) => {
|
|
if (newLocale === locale) return
|
|
router.replace(pathname, { locale: newLocale })
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'px-1 py-1 flex items-center gap-1 rounded-button',
|
|
'bg-nothing-gray-100 border border-nothing-gray-200',
|
|
className
|
|
)}
|
|
>
|
|
{locales.map((loc) => {
|
|
const isActive = loc === locale
|
|
|
|
return (
|
|
<button
|
|
key={loc}
|
|
onClick={() => handleSwitch(loc)}
|
|
className={cn(
|
|
'px-3 py-1.5 rounded-sm font-body text-xs transition-all duration-200',
|
|
'focus:outline-none focus:ring-2 focus:ring-nothing-gray-400',
|
|
isActive
|
|
? 'bg-nothing-gray-900 text-nothing-white'
|
|
: 'text-nothing-gray-600 hover:text-nothing-gray-900 hover:bg-nothing-gray-200'
|
|
)}
|
|
aria-label={`Switch to ${localeShortNames[loc]}`}
|
|
aria-pressed={isActive}
|
|
>
|
|
{localeShortNames[loc]}
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* LanguageSwitcherCompact - 更緊湊的版本
|
|
* 只顯示當前語系,點擊切換
|
|
*/
|
|
export function LanguageSwitcherCompact({ className }: LanguageSwitcherProps) {
|
|
const locale = useLocale() as Locale
|
|
const router = useRouter()
|
|
const pathname = usePathname()
|
|
|
|
const handleToggle = () => {
|
|
const currentIndex = locales.indexOf(locale)
|
|
const nextIndex = (currentIndex + 1) % locales.length
|
|
const newLocale = locales[nextIndex]
|
|
|
|
router.replace(pathname, { locale: newLocale })
|
|
}
|
|
|
|
return (
|
|
<button
|
|
onClick={handleToggle}
|
|
className={cn(
|
|
'px-3 py-1.5 rounded-button',
|
|
'bg-nothing-gray-100 border border-nothing-gray-200',
|
|
'font-body text-xs text-nothing-gray-600',
|
|
'hover:text-nothing-gray-900 hover:bg-nothing-gray-200',
|
|
'transition-all duration-200',
|
|
'focus:outline-none focus:ring-2 focus:ring-nothing-gray-400',
|
|
className
|
|
)}
|
|
aria-label="Toggle language"
|
|
>
|
|
<span className="flex items-center gap-2">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-status-healthy" />
|
|
{localeShortNames[locale]}
|
|
</span>
|
|
</button>
|
|
)
|
|
}
|