LanguageSwitcher.jsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { useState, useRef, useEffect } from 'react'
  2. import { useRouter } from 'next/router'
  3. import Link from 'next/link'
  4. import { useLocale } from '@/contexts/LocaleContext'
  5. const languages = [
  6. { code: 'en', name: 'English', flag: '🇺🇸' },
  7. { code: 'ja', name: '日本語', flag: '🇯🇵' },
  8. { code: 'ko', name: '한국어', flag: '🇰🇷' },
  9. ]
  10. export function LanguageSwitcher() {
  11. const { locale } = useLocale()
  12. const { pathname } = useRouter()
  13. const [isOpen, setIsOpen] = useState(false)
  14. const dropdownRef = useRef(null)
  15. // Close dropdown when clicking outside
  16. useEffect(() => {
  17. const handleClickOutside = (event) => {
  18. if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
  19. setIsOpen(false)
  20. }
  21. }
  22. document.addEventListener('mousedown', handleClickOutside)
  23. return () => document.removeEventListener('mousedown', handleClickOutside)
  24. }, [])
  25. const getLocalizedPath = (targetLocale) => {
  26. if (targetLocale === 'en') {
  27. // For English, remove any locale prefix and return root path
  28. if (pathname.startsWith('/ja')) {
  29. return pathname.replace(/^\/ja/, '') || '/'
  30. }
  31. if (pathname.startsWith('/ko')) {
  32. return pathname.replace(/^\/ko/, '') || '/'
  33. }
  34. return pathname
  35. } else {
  36. // For other locales, add the locale prefix
  37. if (pathname.startsWith('/ja') || pathname.startsWith('/ko')) {
  38. // Replace existing locale prefix
  39. return pathname.replace(/^\/[a-z]{2}/, `/${targetLocale}`)
  40. } else {
  41. // Add locale prefix to English path
  42. return `/${targetLocale}${pathname === '/' ? '' : pathname}`
  43. }
  44. }
  45. }
  46. const currentLanguage = languages.find(lang => lang.code === locale)
  47. return (
  48. <div className="relative" ref={dropdownRef}>
  49. <button
  50. onClick={() => setIsOpen(!isOpen)}
  51. className="flex items-center space-x-1 px-2 py-1 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300 transition-colors"
  52. aria-label="Select language"
  53. >
  54. <span className="text-base">{currentLanguage?.flag}</span>
  55. <svg
  56. className={`w-3 h-3 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
  57. fill="none"
  58. stroke="currentColor"
  59. viewBox="0 0 24 24"
  60. >
  61. <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
  62. </svg>
  63. </button>
  64. {isOpen && (
  65. <div className="absolute right-0 top-full mt-1 w-32 bg-white dark:bg-slate-800 rounded-md shadow-lg border border-slate-200 dark:border-slate-700 py-1 z-50">
  66. {languages.map((lang) => (
  67. <Link
  68. key={lang.code}
  69. href={getLocalizedPath(lang.code)}
  70. onClick={() => setIsOpen(false)}
  71. className={`flex items-center space-x-2 px-3 py-2 text-sm hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors ${
  72. locale === lang.code
  73. ? 'text-blue-600 dark:text-blue-400 font-medium'
  74. : 'text-slate-700 dark:text-slate-300'
  75. }`}
  76. >
  77. <span>{lang.flag}</span>
  78. <span>{lang.name}</span>
  79. </Link>
  80. ))}
  81. </div>
  82. )}
  83. </div>
  84. )
  85. }