Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
365f311
fix: enabled language switcher dbot
rupato-deriv Oct 16, 2025
7d2aaf1
fix: param based language switching
rupato-deriv Oct 16, 2025
63bfc95
fix: Rupato/Enable--language-switcher-on-dbot
rupato-deriv Oct 16, 2025
e37e52b
fix: claude comments
rupato-deriv Oct 16, 2025
cb1056a
fix: trigger cloudflare
rupato-deriv Oct 17, 2025
1a72c97
Merge branch 'master' of github.com:deriv-com/derivatives-bot into Ru…
rupato-deriv Oct 23, 2025
d2dce2e
fix: added error map for backend messages translation issues and run…
rupato-deriv Oct 23, 2025
afa4e24
fix: bug fixes for the translations
rupato-deriv Oct 24, 2025
c25b724
fix: bug fixes for the translations
rupato-deriv Oct 24, 2025
bc1bfc8
fix: bug fixes for the translations
rupato-deriv Oct 24, 2025
342acdc
fix: translations bugs
rupato-deriv Oct 27, 2025
2b41fec
fix: bug fixes for translations
rupato-deriv Oct 28, 2025
785ad35
fix: added batch 2 for translations
rupato-deriv Oct 28, 2025
2e73fb4
fix: translations bugs
rupato-deriv Oct 29, 2025
4a5341c
fix: translations bugs
rupato-deriv Oct 29, 2025
ce653e4
fix: translations bugs
rupato-deriv Oct 29, 2025
b52b345
fix: translations bugs
rupato-deriv Oct 29, 2025
3daeaa3
fix: comments on css
rupato-deriv Oct 29, 2025
6392ed5
fix: comments on css
rupato-deriv Oct 29, 2025
52f8ce9
fix: comments on css
rupato-deriv Oct 29, 2025
724fc28
fix: comments on css
rupato-deriv Oct 29, 2025
4783dd0
fix: comments on css
rupato-deriv Oct 29, 2025
783d786
fix: comments on css
rupato-deriv Oct 29, 2025
da43f1a
fix: comments on css
rupato-deriv Oct 29, 2025
5ba3ae0
fix: comments on css
rupato-deriv Oct 30, 2025
da6dd94
fix: translations for strategy names
rupato-deriv Oct 30, 2025
203861c
fix: security issues
rupato-deriv Oct 30, 2025
1fa7bad
fix: security issues
rupato-deriv Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ 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 { FILTERED_LANGUAGES } from '@/utils/languages';
import { initializeI18n, localize, TranslationProvider, useTranslations } from '@deriv-com/translations';
import CoreStoreProvider from './CoreStoreProvider';
import './app-root.scss';

Expand All @@ -22,6 +23,39 @@ 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 langCodeCandidate = langParam.toUpperCase();
// Use FILTERED_LANGUAGES instead of hard-coded array
const supportedLangCodes = FILTERED_LANGUAGES.map(lang => lang.code);

if (supportedLangCodes.includes(langCodeCandidate)) {
// Type assertion is safe here since we've validated the value
const langCode = langCodeCandidate as (typeof FILTERED_LANGUAGES)[number]['code'];
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]);

return <>{children}</>;
};

const router = createBrowserRouter(
createRoutesFromElements(
<Route
Expand All @@ -31,14 +65,16 @@ const router = createBrowserRouter(
fallback={<ChunkLoader message={localize('Please wait while we connect to the server...')} />}
>
<TranslationProvider defaultLang='EN' i18nInstance={i18nInstance}>
<StoreProvider>
<LocalStorageSyncWrapper>
<RoutePromptDialog />
<CoreStoreProvider>
<Layout />
</CoreStoreProvider>
</LocalStorageSyncWrapper>
</StoreProvider>
<LanguageHandler>
<StoreProvider>
<LocalStorageSyncWrapper>
<RoutePromptDialog />
<CoreStoreProvider>
<Layout />
</CoreStoreProvider>
</LocalStorageSyncWrapper>
</StoreProvider>
</LanguageHandler>
</TranslationProvider>
</Suspense>
}
Expand Down
8 changes: 5 additions & 3 deletions src/components/bot-stopped.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { observer } from 'mobx-react-lite';
import Text from '@/components/shared_ui/text';
import { useStore } from '@/hooks/useStore';
import { generateUrlWithRedirect } from '@/utils/url-redirect-utils';
import { reloadPage, navigateToUrl } from '@/utils/navigation-utils';
import { LegacyClose1pxIcon } from '@deriv/quill-icons/Legacy';
import { Localize, localize } from '@deriv-com/translations';
import Dialog from './shared_ui/dialog';
Expand All @@ -12,7 +13,7 @@ const BotStopped = observer(() => {
const { dashboard } = useStore();
const { is_web_socket_intialised } = dashboard;
const onClickClose = () => {
location.reload();
reloadPage();
};
return (
<Dialog
Expand All @@ -21,8 +22,9 @@ const BotStopped = observer(() => {
className={'dc-dialog bot-stopped-dialog'}
cancel_button_text={localize('Go to Reports')}
confirm_button_text={localize('Back to Bot')}
onCancel={() => (window.location.href = generateUrlWithRedirect(standalone_routes.positions))}
onConfirm={() => location.reload()}
onCancel={() => navigateToUrl(generateUrlWithRedirect(standalone_routes.positions))}
onConfirm={reloadPage}
login={() => {}} // Empty function as login is not needed for this dialog
>
<div className='dc-dialog__content__header'>
<Text data-testid='data-title' weight='bold' as='p' align='left' size='s' color='prominent'>
Expand Down
6 changes: 3 additions & 3 deletions src/components/journal/journal-components/format-message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ const FormatMessage = ({ logType, className, extra }: TFormatMessageProps) => {
return localize('Resale of this contract is not offered.');
}
case LogTypes.PURCHASE: {
const { longcode, transaction_id } = extra;
const { transaction_id } = extra;
return (
<Localize
i18n_default_text='<0>Bought</0>: {{longcode}} (ID: {{transaction_id}})'
values={{ longcode, transaction_id }}
i18n_default_text='<0>Bought</0>: Contract purchased (ID: {{transaction_id}})'
values={{ transaction_id }}
components={[<Text key={0} size='xxs' styles={{ color: 'var(--status-info)' }} />]}
options={{ interpolation: { escapeValue: false } }}
/>
Expand Down
7 changes: 5 additions & 2 deletions src/components/layout/footer/LanguageSettings.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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]
);

Expand All @@ -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}
<Text size='xs' weight='bold'>
Expand Down
43 changes: 25 additions & 18 deletions src/components/layout/footer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,55 @@
// 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 (
<footer className='app-footer'>
<FullScreen />
{/* <LanguageSettings openLanguageSettingModal={openLanguageSettingModal} />
<div className='app-footer__vertical-line' /> */}
<LanguageSettings openLanguageSettingModal={openLanguageSettingModal} />
<div className='app-footer__vertical-line' />
<ChangeTheme />
<div className='app-footer__vertical-line' />
<ServerTime />
<div className='app-footer__vertical-line' />
<NetworkStatus />
<Endpoint />

{/* {isModalOpenFor('DesktopLanguagesModal') && (
{isModalOpenFor('DesktopLanguagesModal') && (
<DesktopLanguagesModal
headerTitle={localize('Select Language')}
isModalOpen
languages={LANGUAGES}
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
// Use replace() to navigate to the active tab URL which will reload the page
window.location.replace(getActiveTabUrl());
} catch (error) {
console.error('Failed to switch language:', error);
hideModal();
}
}}
selectedLanguage={currentLang}
/>
)} */}
)}
</footer>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/components/layout/header/account-switcher.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
display: flex;
flex-direction: column;
justify-content: center;
margin-inline-start: 1rem;
}

&__account-type-header {
Expand Down
4 changes: 2 additions & 2 deletions src/components/layout/header/account-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import classNames from 'classnames';
import { observer } from 'mobx-react-lite';
import { getCurrencyDisplayCode } from '@/components/shared';
import Text from '@/components/shared_ui/text';
import { Localize, localize } from '@deriv-com/translations';
import { Localize } from '@deriv-com/translations';
import { useDevice } from '@deriv-com/ui';
import { TAccountSwitcher } from './common/types';
import AccountInfoIcon from './account-info-icon';
Expand Down Expand Up @@ -43,7 +43,7 @@ const AccountSwitcher = observer(({ activeAccount }: TAccountSwitcher) => {
<div className='acc-info__content'>
<div className='acc-info__account-type-header'>
<Text as='p' size='xxxs' className='acc-info__account-type'>
{isVirtual ? localize('Demo') : localize('Real')}
{isVirtual ? 'Demo' : 'Real'}
</Text>
</div>
{(typeof balance !== 'undefined' || !currency) && (
Expand Down
12 changes: 9 additions & 3 deletions src/components/layout/header/mobile-menu/menu-header.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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]
);

Expand All @@ -24,7 +24,13 @@ const MenuHeader = ({ hideLanguageSetting, openLanguageSetting }: TMenuHeader) =
</Text>

{!hideLanguageSetting && (
<button className='mobile-menu__header__language items-center' onClick={openLanguageSetting}>
<button
className='mobile-menu__header__language items-center'
onClick={openLanguageSetting}
aria-label={`${localize('Change language')} - ${localize('Current language')}: ${currentLang}`}
aria-expanded='false'
aria-haspopup='menu'
>
{countryIcon}
<Text className='ml-[0.4rem]' size={isDesktop ? 'xs' : 'sm'} weight='bold'>
{currentLang}
Expand Down
44 changes: 40 additions & 4 deletions src/components/layout/header/mobile-menu/mobile-menu.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -17,7 +20,8 @@ type TMobileMenuProps = {
const MobileMenu = ({ onLogout }: TMobileMenuProps) => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [activeSubmenu, setActiveSubmenu] = useState<string | null>(null);
const { localize } = useTranslations();
const { currentLang = 'EN', localize, switchLanguage } = useTranslations();
const { hideModal, isModalOpenFor, showModal } = useModalManager();
const { isDesktop } = useDevice();

const openDrawer = () => setIsDrawerOpen(true);
Expand All @@ -28,6 +32,8 @@ 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 (
Expand All @@ -38,11 +44,41 @@ const MobileMenu = ({ onLogout }: TMobileMenuProps) => {

<Drawer isOpen={isDrawerOpen} onCloseDrawer={closeDrawer} width='29.5rem'>
<Drawer.Header onCloseDrawer={closeDrawer}>
<MenuHeader hideLanguageSetting={true} openLanguageSetting={() => {}} />
<MenuHeader
hideLanguageSetting={isLanguageSettingVisible}
openLanguageSetting={openLanguageSetting}
/>
</Drawer.Header>

<Drawer.Content>
{activeSubmenu === 'reports' ? (
{isLanguageSettingVisible ? (
<>
<div className='mobile-menu__back-btn'>
<BackButton buttonText={localize('Language')} onClick={hideModal} />
</div>

<MobileLanguagesDrawer
isOpen
languages={FILTERED_LANGUAGES}
onClose={hideModal}
onLanguageSwitch={code => {
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
// Use replace() to navigate to the active tab URL which will reload the page
window.location.replace(getActiveTabUrl());
} catch (error) {
console.error('Failed to switch language:', error);
hideModal();
}
}}
selectedLanguage={currentLang}
wrapperClassName='mobile-menu__language-drawer'
/>
</>
) : activeSubmenu === 'reports' ? (
<>
<div className='mobile-menu__back-btn'>
<BackButton buttonText={localize('Reports')} onClick={closeSubmenu} />
Expand Down
3 changes: 1 addition & 2 deletions src/components/layout/header/wallets/wallet-badge.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import Badge from '@/components/shared_ui/badge';
import { localize } from '@deriv-com/translations';

type TWalletBadge = {
is_demo: boolean;
Expand All @@ -9,7 +8,7 @@ type TWalletBadge = {

const WalletBadge = ({ is_demo, label }: TWalletBadge) => {
return is_demo ? (
<Badge type='contained' background_color='blue' label={localize('Demo')} custom_color='colored-background' />
<Badge type='contained' background_color='blue' label='Demo' custom_color='colored-background' />
) : (
<Badge type='bordered' label={label?.toUpperCase() ?? ''} />
);
Expand Down
Loading