diff --git a/apps/what-today/src/components/FlagIcon.tsx b/apps/what-today/src/components/FlagIcon.tsx index 44be7551..785de77a 100644 --- a/apps/what-today/src/components/FlagIcon.tsx +++ b/apps/what-today/src/components/FlagIcon.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { twMerge } from 'tailwind-merge'; interface FlagIconProps { flagCode: string; @@ -19,7 +20,7 @@ const FlagIcon: React.FC = ({ flagCode, alt = 'Flag', className = return ( {alt} { // 이미지 로드 실패 시 기본 아이콘으로 대체 diff --git a/apps/what-today/src/components/FloatingTranslateButton.tsx b/apps/what-today/src/components/FloatingTranslateButton.tsx index 59026a51..c3c3f27a 100644 --- a/apps/what-today/src/components/FloatingTranslateButton.tsx +++ b/apps/what-today/src/components/FloatingTranslateButton.tsx @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { useToast } from '@what-today/design-system'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { twMerge } from 'tailwind-merge'; @@ -15,6 +16,7 @@ interface FloatingTranslateButtonProps { } const FloatingTranslateButton: React.FC = ({ className }) => { + const { toast } = useToast(); const [selectedLanguage, setSelectedLanguage] = useState(languages[0]); const [isReady, setIsReady] = useState(false); const [currentTranslatedLang, setCurrentTranslatedLang] = useState('ko'); @@ -25,11 +27,21 @@ const FloatingTranslateButton: React.FC = ({ class // 현재 번역된 언어 감지 const detectCurrentLanguage = useCallback(() => { try { - // URL에서 현재 번역 언어 감지 + // 우선 googtrans 쿠키 확인 + const match = document.cookie.match(/(?:^|;\s*)googtrans=\/[a-z]+\/([a-z]+)/); + if (match && match[1]) { + const detectedLang = match[1]; + setCurrentTranslatedLang(detectedLang); + const foundLang = findLanguageByCode(detectedLang); + if (foundLang) { + setSelectedLanguage(foundLang); + } + return; + } + + // fallback: URL 파라미터 확인 const urlParams = new URLSearchParams(window.location.search); const langFromUrl = urlParams.get('lang'); - - // Google Translate가 URL에 추가하는 언어 코드 확인 if (langFromUrl) { setCurrentTranslatedLang(langFromUrl); const foundLang = findLanguageByCode(langFromUrl); @@ -39,7 +51,7 @@ const FloatingTranslateButton: React.FC = ({ class return; } - // body 클래스에서 번역 상태 확인 + // fallback: body class 확인 const bodyClasses = document.body.className; if (bodyClasses.includes('translated-')) { const matches = bodyClasses.match(/translated-(\w+)/); @@ -52,10 +64,16 @@ const FloatingTranslateButton: React.FC = ({ class } } } - } catch (error) { - console.warn('언어 감지 중 오류:', error); + } catch { + toast({ + title: '번역 오류', + description: '언어 감지 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); } - }, []); + }, [toast]); // Google Translate 위젯 초기화 const initializeGoogleTranslate = useCallback(() => { @@ -88,11 +106,17 @@ const FloatingTranslateButton: React.FC = ({ class detectCurrentLanguage(); }, 300); } - } catch (error) { - console.error('Google Translate 초기화 실패:', error); + } catch { + toast({ + title: '번역 오류', + description: '번역 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); initializationAttempted.current = false; } - }, [detectCurrentLanguage]); + }, [detectCurrentLanguage, toast]); // Google Translate 스크립트 로드 useEffect(() => { @@ -123,12 +147,18 @@ const FloatingTranslateButton: React.FC = ({ class script.src = 'https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit'; script.async = true; script.onerror = () => { - console.error('Google Translate 스크립트 로드 실패'); + toast({ + title: '번역 오류', + description: '번역 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); isGoogleTranslateLoading = false; }; document.head.appendChild(script); } - }, [initializeGoogleTranslate]); + }, [initializeGoogleTranslate, toast]); // URL 변경 감지 (번역 후 URL이 변경되므로) useEffect(() => { @@ -165,7 +195,13 @@ const FloatingTranslateButton: React.FC = ({ class const changeLanguage = useCallback( (language: Language) => { if (!isReady) { - console.warn('Google Translate가 아직 준비되지 않았습니다.'); + toast({ + title: '번역 오류', + description: '번역 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); return; } @@ -221,8 +257,14 @@ const FloatingTranslateButton: React.FC = ({ class // ✅ 최종: URL 파라미터 제거 & 페이지 새로고침 const newUrl = window.location.origin + window.location.pathname; window.location.href = newUrl; - } catch (error) { - console.error('원문 복귀 실패:', error); + } catch { + toast({ + title: '번역 오류', + description: '번역 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); } return; @@ -250,16 +292,28 @@ const FloatingTranslateButton: React.FC = ({ class setIsOpen(false); } } else { - console.error('Google Translate select 요소를 찾을 수 없습니다.'); + toast({ + title: '번역 오류', + description: '번역 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); // 초기화 재시도 initializationAttempted.current = false; setTimeout(() => initializeGoogleTranslate(), 500); } - } catch (error) { - console.error('언어 변경 중 오류:', error); + } catch { + toast({ + title: '번역 오류', + description: '번역 오류로 새로고침합니다.', + }); + setTimeout(() => { + window.location.reload(); + }, 1000); } }, - [isReady, currentTranslatedLang, initializeGoogleTranslate], + [isReady, currentTranslatedLang, initializeGoogleTranslate, toast], ); // 버튼 클릭 핸들러 @@ -273,12 +327,12 @@ const FloatingTranslateButton: React.FC = ({ class
{/* 플로팅 번역 버튼 */} -
+
{/* 언어 선택 드롭다운 */} {isOpen && ( -
-
언어 선택
-
+
+
언어 선택
+
{languages.map((language) => ( ))} @@ -301,13 +355,11 @@ const FloatingTranslateButton: React.FC = ({ class {/* 플로팅 버튼 */}
diff --git a/apps/what-today/src/constants/languages.ts b/apps/what-today/src/constants/languages.ts index 573b9053..a29796fb 100644 --- a/apps/what-today/src/constants/languages.ts +++ b/apps/what-today/src/constants/languages.ts @@ -7,16 +7,16 @@ export interface Language { export const languages: Language[] = [ { code: 'ko', name: '한국어', flag: 'kr' }, // 한국어 { code: 'en', name: 'English', flag: 'us' }, // 영어 - { code: 'fr', name: 'Français', flag: 'fr' }, // 프랑스어 + { code: 'ja', name: '日本語', flag: 'jp' }, // 일본어 { code: 'de', name: 'Deutsch', flag: 'de' }, // 독일어 + { code: 'hi', name: 'हिन्दी', flag: 'in' }, // 힌디어 + { code: 'fr', name: 'Français', flag: 'fr' }, // 프랑스어 { code: 'zh-TW', name: '中文(繁體)', flag: 'tw' }, // 중국어(번체, 대만) - { code: 'ja', name: '日本語', flag: 'jp' }, // 일본어 { code: 'es', name: 'Español', flag: 'es' }, // 스페인어 { code: 'ru', name: 'Русский', flag: 'ru' }, // 러시아어 { code: 'it', name: 'Italiano', flag: 'it' }, // 이탈리아어 { code: 'pt', name: 'Português', flag: 'pt' }, // 포르투갈어 { code: 'ar', name: 'العربية', flag: 'sa' }, // 아랍어 - { code: 'hi', name: 'हिन्दी', flag: 'in' }, // 힌디어 { code: 'th', name: 'ไทย', flag: 'th' }, // 태국어 { code: 'tr', name: 'Türkçe', flag: 'tr' }, // 터키어 { code: 'el', name: 'Ελληνικά', flag: 'gr' }, // 그리스어