language-switcher.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. "use client"
  2. import { useRouter, usePathname } from 'next/navigation'
  3. import { useLocale } from 'next-intl'
  4. import { Button } from '@/components/ui/button'
  5. import {
  6. DropdownMenu,
  7. DropdownMenuContent,
  8. DropdownMenuItem,
  9. DropdownMenuTrigger,
  10. } from '@/components/ui/dropdown-menu'
  11. import { GlobeIcon, CheckIcon } from 'lucide-react'
  12. import { useState, useEffect } from 'react'
  13. const languages = [
  14. { code: 'en', name: 'English', flag: '🇺🇸' },
  15. { code: 'zh', name: '简体中文', flag: '🇨🇳' },
  16. ]
  17. export default function LanguageSwitcher() {
  18. const router = useRouter()
  19. const pathname = usePathname()
  20. const currentLocale = useLocale()
  21. const [mounted, setMounted] = useState(false)
  22. useEffect(() => {
  23. setMounted(true)
  24. }, [])
  25. const switchLanguage = (locale: string) => {
  26. // 获取当前路径,移除语言前缀
  27. const segments = pathname.split('/').filter(Boolean)
  28. const pathWithoutLocale = segments.slice(1).join('/')
  29. // 构建新的路径 - 所有语言都有前缀
  30. const newPath = `/${locale}/${pathWithoutLocale}`.replace(/\/+$/, '') || `/${locale}`
  31. // 导航到新路径
  32. router.push(newPath)
  33. // 保存用户选择的语言到localStorage
  34. if (typeof window !== 'undefined') {
  35. localStorage.setItem('preferred-language', locale)
  36. }
  37. }
  38. const getCurrentLanguage = () => {
  39. return languages.find(lang => lang.code === currentLocale) || languages[0]
  40. }
  41. // 自动检测浏览器语言(仅在首次访问时)
  42. useEffect(() => {
  43. if (mounted && typeof window !== 'undefined') {
  44. const savedLanguage = localStorage.getItem('preferred-language')
  45. if (!savedLanguage) {
  46. // 检测浏览器语言
  47. const browserLang = navigator.language.toLowerCase()
  48. let detectedLang = 'en' // 默认语言
  49. if (browserLang.startsWith('zh')) {
  50. detectedLang = 'zh'
  51. }
  52. // 如果检测到的语言与当前语言不同,则切换
  53. if (detectedLang !== currentLocale && detectedLang !== 'en') {
  54. switchLanguage(detectedLang)
  55. }
  56. }
  57. }
  58. }, [mounted, currentLocale])
  59. if (!mounted) {
  60. return null
  61. }
  62. const currentLang = getCurrentLanguage()
  63. return (
  64. <DropdownMenu>
  65. <DropdownMenuTrigger asChild>
  66. <Button variant="ghost" size="sm" className="gap-2">
  67. <GlobeIcon className="h-4 w-4" />
  68. <span className="hidden sm:inline">{currentLang.flag} {currentLang.name}</span>
  69. <span className="sm:hidden">{currentLang.flag}</span>
  70. </Button>
  71. </DropdownMenuTrigger>
  72. <DropdownMenuContent align="end" className="w-40">
  73. {languages.map((language) => (
  74. <DropdownMenuItem
  75. key={language.code}
  76. onClick={() => switchLanguage(language.code)}
  77. className="flex items-center justify-between cursor-pointer"
  78. >
  79. <span className="flex items-center gap-2">
  80. <span>{language.flag}</span>
  81. <span>{language.name}</span>
  82. </span>
  83. {language.code === currentLocale && (
  84. <CheckIcon className="h-4 w-4 text-primary" />
  85. )}
  86. </DropdownMenuItem>
  87. ))}
  88. </DropdownMenuContent>
  89. </DropdownMenu>
  90. )
  91. }