From 365f3113e53261a120508e3e200ce3bd1eb6a0da Mon Sep 17 00:00:00 2001 From: Rupato Braganza Date: Thu, 16 Oct 2025 13:35:28 +0800 Subject: [PATCH 1/9] fix: enabled language switcher dbot --- src/components/layout/footer/index.tsx | 28 +++++++++---------- .../layout/header/mobile-menu/mobile-menu.tsx | 26 +++++++++++++++-- src/utils/languages.tsx | 5 ++++ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/components/layout/footer/index.tsx b/src/components/layout/footer/index.tsx index 75483c8f..59e8989c 100644 --- a/src/components/layout/footer/index.tsx +++ b/src/components/layout/footer/index.tsx @@ -1,26 +1,26 @@ -// import useModalManager from '@/hooks/useModalManager'; -// import { getActiveTabUrl } from '@/utils/getActiveTabUrl'; -// import { LANGUAGES } from '@/utils/languages'; -// import { useTranslations } from '@deriv-com/translations'; -// import { DesktopLanguagesModal } from '@deriv-com/ui'; +import useModalManager from '@/hooks/useModalManager'; +import { getActiveTabUrl } from '@/utils/getActiveTabUrl'; +import { FILTERED_LANGUAGES } from '@/utils/languages'; +import { useTranslations } from '@deriv-com/translations'; +import { DesktopLanguagesModal } from '@deriv-com/ui'; import ChangeTheme from './ChangeTheme'; import Endpoint from './Endpoint'; import FullScreen from './FullScreen'; -// import LanguageSettings from './LanguageSettings'; +import LanguageSettings from './LanguageSettings'; import NetworkStatus from './NetworkStatus'; import ServerTime from './ServerTime'; import './footer.scss'; const Footer = () => { - // const { currentLang = 'EN', localize, switchLanguage } = useTranslations(); - // const { hideModal, isModalOpenFor, showModal } = useModalManager(); + const { currentLang = 'EN', localize, switchLanguage } = useTranslations(); + const { hideModal, isModalOpenFor, showModal } = useModalManager(); - // const openLanguageSettingModal = () => showModal('DesktopLanguagesModal'); + const openLanguageSettingModal = () => showModal('DesktopLanguagesModal'); return ( ); }; diff --git a/src/components/layout/header/mobile-menu/mobile-menu.tsx b/src/components/layout/header/mobile-menu/mobile-menu.tsx index 9717fcbc..df58abc5 100644 --- a/src/components/layout/header/mobile-menu/mobile-menu.tsx +++ b/src/components/layout/header/mobile-menu/mobile-menu.tsx @@ -1,6 +1,9 @@ import { useState } from 'react'; +import useModalManager from '@/hooks/useModalManager'; +import { getActiveTabUrl } from '@/utils/getActiveTabUrl'; +import { FILTERED_LANGUAGES } from '@/utils/languages'; import { useTranslations } from '@deriv-com/translations'; -import { Drawer, useDevice } from '@deriv-com/ui'; +import { Drawer, MobileLanguagesDrawer,useDevice } from '@deriv-com/ui'; import NetworkStatus from './../../footer/NetworkStatus'; import ServerTime from './../../footer/ServerTime'; import BackButton from './back-button'; @@ -17,7 +20,8 @@ type TMobileMenuProps = { const MobileMenu = ({ onLogout }: TMobileMenuProps) => { const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [activeSubmenu, setActiveSubmenu] = useState(null); - const { localize } = useTranslations(); + const { currentLang = 'EN', localize, switchLanguage } = useTranslations(); + const { hideModal, isModalOpenFor, showModal } = useModalManager(); const { isDesktop } = useDevice(); const openDrawer = () => setIsDrawerOpen(true); @@ -28,6 +32,7 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => { const openSubmenu = (submenu: string) => setActiveSubmenu(submenu); const closeSubmenu = () => setActiveSubmenu(null); + const openLanguageSetting = () => showModal('MobileLanguagesDrawer'); if (isDesktop) return null; return ( @@ -38,7 +43,7 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => { - {}} /> + @@ -65,6 +70,21 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => { + + {isModalOpenFor('MobileLanguagesDrawer') && ( + { + switchLanguage(code); + hideModal(); + window.location.replace(getActiveTabUrl()); + window.location.reload(); + }} + selectedLanguage={currentLang} + /> + )} ); }; diff --git a/src/utils/languages.tsx b/src/utils/languages.tsx index 9a77ac8a..d808eae6 100644 --- a/src/utils/languages.tsx +++ b/src/utils/languages.tsx @@ -165,3 +165,8 @@ export const LANGUAGES = [ placeholderIconInMobile: , }, ]; + +// Filtered languages for the language switcher (only show specific languages) +export const FILTERED_LANGUAGES = LANGUAGES.filter(lang => + ['EN', 'ES', 'FR', 'PT', 'AR', 'IT', 'RU'].includes(lang.code) +); From 7d2aaf15d11c4423e388b10f7a3f5d2739c04403 Mon Sep 17 00:00:00 2001 From: Rupato Braganza Date: Thu, 16 Oct 2025 13:39:37 +0800 Subject: [PATCH 2/9] fix: param based language switching --- src/app/App.tsx | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index 039e85fb..478da502 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -10,7 +10,7 @@ import { StoreProvider } from '@/hooks/useStore'; import CallbackPage from '@/pages/callback'; import Endpoint from '@/pages/endpoint'; import { TAuthData } from '@/types/api-types'; -import { initializeI18n, localize, TranslationProvider } from '@deriv-com/translations'; +import { initializeI18n, localize, TranslationProvider, useTranslations } from '@deriv-com/translations'; import CoreStoreProvider from './CoreStoreProvider'; import './app-root.scss'; @@ -22,6 +22,28 @@ const i18nInstance = initializeI18n({ cdnUrl: `${TRANSLATIONS_CDN_URL || 'https://translations.deriv.com'}/${R2_PROJECT_NAME}/${CROWDIN_BRANCH_NAME}`, }); +// Component to handle language URL parameter +const LanguageHandler = ({ children }: { children: React.ReactNode }) => { + const { switchLanguage } = useTranslations(); + + React.useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const langParam = urlParams.get('lang'); + + if (langParam) { + // Convert to uppercase to match our language codes + const langCode = langParam.toUpperCase(); + // Only switch if it's one of our supported filtered languages + const supportedLangs = ['EN', 'ES', 'FR', 'PT', 'AR', 'IT', 'RU']; + if (supportedLangs.includes(langCode)) { + switchLanguage(langCode); + } + } + }, [switchLanguage]); + + return <>{children}; +}; + const router = createBrowserRouter( createRoutesFromElements( } > - - - - - - - - + + + + + + + + + + } From 63bfc9555fb5099369a7a644971674351e9f50ae Mon Sep 17 00:00:00 2001 From: Rupato Braganza Date: Thu, 16 Oct 2025 14:14:52 +0800 Subject: [PATCH 3/9] fix: Rupato/Enable--language-switcher-on-dbot --- .../layout/header/mobile-menu/menu-header.tsx | 4 +- .../layout/header/mobile-menu/mobile-menu.tsx | 45 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/components/layout/header/mobile-menu/menu-header.tsx b/src/components/layout/header/mobile-menu/menu-header.tsx index 98e509ea..50aa16cf 100644 --- a/src/components/layout/header/mobile-menu/menu-header.tsx +++ b/src/components/layout/header/mobile-menu/menu-header.tsx @@ -1,5 +1,5 @@ import { ComponentProps, useMemo } from 'react'; -import { LANGUAGES } from '@/utils/languages'; +import { FILTERED_LANGUAGES } from '@/utils/languages'; import { useTranslations } from '@deriv-com/translations'; import { Text, useDevice } from '@deriv-com/ui'; @@ -13,7 +13,7 @@ const MenuHeader = ({ hideLanguageSetting, openLanguageSetting }: TMenuHeader) = const { isDesktop } = useDevice(); const countryIcon = useMemo( - () => LANGUAGES.find(({ code }) => code === currentLang)?.placeholderIconInMobile, + () => FILTERED_LANGUAGES.find(({ code }) => code === currentLang)?.placeholderIconInMobile, [currentLang] ); diff --git a/src/components/layout/header/mobile-menu/mobile-menu.tsx b/src/components/layout/header/mobile-menu/mobile-menu.tsx index df58abc5..e488fb24 100644 --- a/src/components/layout/header/mobile-menu/mobile-menu.tsx +++ b/src/components/layout/header/mobile-menu/mobile-menu.tsx @@ -3,7 +3,7 @@ import useModalManager from '@/hooks/useModalManager'; import { getActiveTabUrl } from '@/utils/getActiveTabUrl'; import { FILTERED_LANGUAGES } from '@/utils/languages'; import { useTranslations } from '@deriv-com/translations'; -import { Drawer, MobileLanguagesDrawer,useDevice } from '@deriv-com/ui'; +import { Drawer, MobileLanguagesDrawer, useDevice } from '@deriv-com/ui'; import NetworkStatus from './../../footer/NetworkStatus'; import ServerTime from './../../footer/ServerTime'; import BackButton from './back-button'; @@ -33,6 +33,7 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => { const openSubmenu = (submenu: string) => setActiveSubmenu(submenu); const closeSubmenu = () => setActiveSubmenu(null); const openLanguageSetting = () => showModal('MobileLanguagesDrawer'); + const isLanguageSettingVisible = Boolean(isModalOpenFor('MobileLanguagesDrawer')); if (isDesktop) return null; return ( @@ -43,11 +44,34 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => { - + - {activeSubmenu === 'reports' ? ( + {isLanguageSettingVisible ? ( + <> +
+ +
+ + { + switchLanguage(code); + hideModal(); + window.location.replace(getActiveTabUrl()); + window.location.reload(); + }} + selectedLanguage={currentLang} + wrapperClassName='mobile-menu__language-drawer' + /> + + ) : activeSubmenu === 'reports' ? ( <>
@@ -70,21 +94,6 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => { - - {isModalOpenFor('MobileLanguagesDrawer') && ( - { - switchLanguage(code); - hideModal(); - window.location.replace(getActiveTabUrl()); - window.location.reload(); - }} - selectedLanguage={currentLang} - /> - )}
); }; From e37e52b3c8815cbe031adb238b1c2640d2be5f20 Mon Sep 17 00:00:00 2001 From: Rupato Braganza Date: Thu, 16 Oct 2025 14:26:28 +0800 Subject: [PATCH 4/9] fix: claude comments --- src/app/App.tsx | 20 ++++++++++++++----- .../layout/footer/LanguageSettings.tsx | 7 +++++-- src/components/layout/footer/index.tsx | 15 ++++++++++---- .../layout/header/mobile-menu/menu-header.tsx | 8 +++++++- .../layout/header/mobile-menu/mobile-menu.tsx | 15 ++++++++++---- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index 478da502..251aec56 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -10,6 +10,7 @@ import { StoreProvider } from '@/hooks/useStore'; import CallbackPage from '@/pages/callback'; import Endpoint from '@/pages/endpoint'; import { TAuthData } from '@/types/api-types'; +import { FILTERED_LANGUAGES } from '@/utils/languages'; import { initializeI18n, localize, TranslationProvider, useTranslations } from '@deriv-com/translations'; import CoreStoreProvider from './CoreStoreProvider'; import './app-root.scss'; @@ -32,11 +33,20 @@ const LanguageHandler = ({ children }: { children: React.ReactNode }) => { if (langParam) { // Convert to uppercase to match our language codes - const langCode = langParam.toUpperCase(); - // Only switch if it's one of our supported filtered languages - const supportedLangs = ['EN', 'ES', 'FR', 'PT', 'AR', 'IT', 'RU']; - if (supportedLangs.includes(langCode)) { - switchLanguage(langCode); + const langCode = langParam.toUpperCase() as (typeof FILTERED_LANGUAGES)[number]['code']; + // Use FILTERED_LANGUAGES instead of hard-coded array + const supportedLangCodes = FILTERED_LANGUAGES.map(lang => lang.code); + + if (supportedLangCodes.includes(langCode)) { + try { + switchLanguage(langCode); + // Remove lang parameter after processing to avoid URL pollution + const url = new URL(window.location.href); + url.searchParams.delete('lang'); + window.history.replaceState({}, '', url.toString()); + } catch (error) { + console.error('Failed to switch language:', error); + } } } }, [switchLanguage]); diff --git a/src/components/layout/footer/LanguageSettings.tsx b/src/components/layout/footer/LanguageSettings.tsx index 91d815a0..49dc2253 100644 --- a/src/components/layout/footer/LanguageSettings.tsx +++ b/src/components/layout/footer/LanguageSettings.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import Text from '@/components/shared_ui/text'; -import { LANGUAGES } from '@/utils/languages'; +import { FILTERED_LANGUAGES } from '@/utils/languages'; import { useTranslations } from '@deriv-com/translations'; import { Tooltip } from '@deriv-com/ui'; @@ -12,7 +12,7 @@ const LanguageSettings = ({ openLanguageSettingModal }: TLanguageSettings) => { const { currentLang, localize } = useTranslations(); const countryIcon = useMemo( - () => LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon, + () => FILTERED_LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon, [currentLang] ); @@ -22,6 +22,9 @@ const LanguageSettings = ({ openLanguageSettingModal }: TLanguageSettings) => { className='app-footer__language' onClick={openLanguageSettingModal} tooltipContent={localize('Language')} + aria-label={`${localize('Change language')} - ${localize('Current language')}: ${currentLang}`} + aria-expanded='false' + aria-haspopup='dialog' > {countryIcon} diff --git a/src/components/layout/footer/index.tsx b/src/components/layout/footer/index.tsx index 59e8989c..3464d064 100644 --- a/src/components/layout/footer/index.tsx +++ b/src/components/layout/footer/index.tsx @@ -35,10 +35,17 @@ const Footer = () => { languages={FILTERED_LANGUAGES} onClose={hideModal} onLanguageSwitch={code => { - switchLanguage(code); - hideModal(); - window.location.replace(getActiveTabUrl()); - window.location.reload(); + try { + switchLanguage(code); + hideModal(); + // Page reload is necessary because Blockly is outside React lifecycle + // and won't re-render with new language without full page refresh + window.location.replace(getActiveTabUrl()); + window.location.reload(); + } catch (error) { + console.error('Failed to switch language:', error); + hideModal(); + } }} selectedLanguage={currentLang} /> diff --git a/src/components/layout/header/mobile-menu/menu-header.tsx b/src/components/layout/header/mobile-menu/menu-header.tsx index 50aa16cf..223a2316 100644 --- a/src/components/layout/header/mobile-menu/menu-header.tsx +++ b/src/components/layout/header/mobile-menu/menu-header.tsx @@ -24,7 +24,13 @@ const MenuHeader = ({ hideLanguageSetting, openLanguageSetting }: TMenuHeader) = {!hideLanguageSetting && ( -