-
Notifications
You must be signed in to change notification settings - Fork 16
fix: Rupato/Enable--language-switcher-on-dbot #156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
🤖 AI Code Analysis Results🌱 AI-Assisted Development🟥⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 4% 2,036 of 55,840 characters (4%) in newly added lines are AI-generated 📊 Analysis Summary
📋 Per-File Breakdown (New Lines Only)
🌱 4% AI-generated code shows some AI assistance in development. 📊 Raw Data (for dashboard){
"timestamp": "2025-10-24T08:29:58.662Z",
"repository": "deriv-com/derivatives-bot",
"branch": "unknown",
"author": "unknown",
"summary": {
"totalCharacters": 55840,
"aiCharacters": 2036,
"percentage": 4
},
"files": [
{
"name": "src/app/App.tsx",
"totalCharacters": 1940,
"aiCharacters": 757,
"percentage": 39,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { FILTERED_LANGUAGES } from '@/utils/languages';\nimport { initializeI18n, localize, TranslationProvider, useTranslations } from '@deriv-com/translations';\n// Component to handle language URL parameter\nconst LanguageHandler = ({ children }: { children: React.ReactNode }) => {\n const { switchLanguage } = useTranslations();\n\n React.useEffect(() => {\n const urlParams = new URLSearchParams(window.location.search);\n const langParam = urlParams.get('lang');\n\n if (langP..."
},
{
"name": "src/components/journal/journal-components/format-message.tsx",
"totalCharacters": 204,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " const { transaction_id } = extra;\n i18n_default_text='<0>Bought</0>: Contract purchased (ID: {{transaction_id}})'\n values={{ transaction_id }}"
},
{
"name": "src/components/layout/footer/LanguageSettings.tsx",
"totalCharacters": 341,
"aiCharacters": 293,
"percentage": 86,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { FILTERED_LANGUAGES } from '@/utils/languages';\n () => FILTERED_LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon,\n aria-label={`${localize('Change language')} - ${localize('Current language')}: ${currentLang}`}\n aria-expanded='false'\n aria-haspopup='dialog'"
},
{
"name": "src/components/layout/footer/index.tsx",
"totalCharacters": 1462,
"aiCharacters": 322,
"percentage": 22,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import useModalManager from '@/hooks/useModalManager';\nimport { getActiveTabUrl } from '@/utils/getActiveTabUrl';\nimport { FILTERED_LANGUAGES } from '@/utils/languages';\nimport { useTranslations } from '@deriv-com/translations';\nimport { DesktopLanguagesModal } from '@deriv-com/ui';\nimport LanguageSettings from './LanguageSettings';\n const { currentLang = 'EN', localize, switchLanguage } = useTranslations();\n const { hideModal, isModalOpenFor, showModal } = useModalManager();\n const ope..."
},
{
"name": "src/components/layout/header/account-switcher.tsx",
"totalCharacters": 113,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { Localize } from '@deriv-com/translations';\n {isVirtual ? 'Demo' : 'Real'}"
},
{
"name": "src/components/layout/header/mobile-menu/menu-header.tsx",
"totalCharacters": 521,
"aiCharacters": 135,
"percentage": 26,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { FILTERED_LANGUAGES } from '@/utils/languages';\n () => FILTERED_LANGUAGES.find(({ code }) => code === currentLang)?.placeholderIconInMobile,\n <button\n className='mobile-menu__header__language items-center'\n onClick={openLanguageSetting}\n aria-label={`${localize('Change language')} - ${localize('Current language')}: ${currentLang}`}\n aria-expanded='false'\n aria-haspopup='me..."
},
{
"name": "src/components/layout/header/mobile-menu/mobile-menu.tsx",
"totalCharacters": 2350,
"aiCharacters": 329,
"percentage": 14,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import useModalManager from '@/hooks/useModalManager';\nimport { getActiveTabUrl } from '@/utils/getActiveTabUrl';\nimport { FILTERED_LANGUAGES } from '@/utils/languages';\nimport { Drawer, MobileLanguagesDrawer, useDevice } from '@deriv-com/ui';\n const { currentLang = 'EN', localize, switchLanguage } = useTranslations();\n const { hideModal, isModalOpenFor, showModal } = useModalManager();\n const openLanguageSetting = () => showModal('MobileLanguagesDrawer');\n const isLanguageSettingVis..."
},
{
"name": "src/components/layout/header/wallets/wallet-badge.tsx",
"totalCharacters": 105,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " <Badge type='contained' background_color='blue' label='Demo' custom_color='colored-background' />"
},
{
"name": "src/components/shared/utils/constants/contract.ts",
"totalCharacters": 6331,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " accumulator: 'Accumulators',\n multiplier: 'Multiplier',\n payout_per_pip: 'Payout per pip',\n payout_per_point: 'Payout per point',\n payout: 'Payout',\n stake: 'Stake',\n turbos: 'Turbos',\n APPLY: 'Apply',\n BARRIER: 'Barrier:',\n BUY_PRICE: 'Buy price:',\n CANCEL: 'Cancel',\n CLOSE: 'Close',\n CLOSED: 'Closed',\n CONTRACT_COST: 'Contract cost:',\n CONTRACT_VALUE: 'Contract value:',\n CURRENT_STA..."
},
{
"name": "src/components/shared_ui/autocomplete/autocomplete.scss",
"totalCharacters": 63,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " inset-inline-end: 0;\n margin-inline-end: 1.1rem;"
},
{
"name": "src/components/shared_ui/input/input.scss",
"totalCharacters": 208,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " margin-inline-start: 1rem;\n inset-inline-start: 0;\n padding-inline-start: calc(1.6rem + 2.4rem);\n padding-inline-start: calc(1.6rem + 3.2rem);\n // right: 0;"
},
{
"name": "src/components/shared_ui/select-native/select-native.scss",
"totalCharacters": 37,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " padding-inline-start: 1.2rem;"
},
{
"name": "src/constants/__tests__/backend-error-messages.spec.ts",
"totalCharacters": 3267,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getBackendErrorMessages, getLocalizedErrorMessage } from '../backend-error-messages';\n\n// Mock the localize function\njest.mock('@deriv-com/translations', () => ({\n localize: jest.fn((message, params) => {\n // Simple mock that replaces {{param}} with actual values\n let result = message;\n if (params) {\n Object.keys(params).forEach(key => {\n result = result.replace(new RegExp(`{{${key}}}`, 'g'), params[key]);\n });\n }\n ..."
},
{
"name": "src/constants/backend-error-messages.ts",
"totalCharacters": 21171,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { localize } from '@deriv-com/translations';\n\n/**\n * Converts backend parameter format [_1], [_2] to frontend format and maps parameters\n * @param message - The backend error message\n * @param details - The error details containing parameter values\n * @returns Processed parameters object\n */\nconst processBackendParameters = (message: string, details?: Record<string, any>) => {\n if (!details) return {};\n\n const params: Record<string, any> = {};\n\n // Handle common parameter mapping..."
},
{
"name": "src/constants/index.ts",
"totalCharacters": 471,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "export * from './backend-error-messages';\nexport * from './bot-version';\nexport * from './button-status';\nexport * from './contract';\nexport * from './contract-stage';\nexport * from './dashboard';\nexport * from './run-panel';\nexport * from './transactions';\nexport * from './z-indexes';\n\n// Export specific items to avoid conflicts\nexport { tabs_title as bot_contents_tabs_title } from './bot-contents';\nexport { tabs_title as load_modal_tabs_title } from './load-modal';"
},
{
"name": "src/external/bot-skeleton/constants/config.ts",
"totalCharacters": 1655,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ACCU: 'Buy',\n MULTUP: 'Up',\n MULTDOWN: 'Down',\n CALL: 'Rise',\n PUT: 'Fall',\n CALLE: 'Rise Equals',\n PUTE: 'Fall Equals',\n CALL: 'Higher',\n PUT: 'Lower',\n ONETOUCH: 'Touch',\n NOTOUCH: 'No Touch',\n EXPIRYRANGE: 'Ends Between',\n EXPIRYMISS: 'Ends Outside',\n RANGE: 'Stays Between',\n ..."
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/List/lists_getIndex.js",
"totalCharacters": 243,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['get', 'GET'],\n ['get and remove', 'GET_REMOVE'],\n ['remove', 'REMOVE'],\n ['# from end', 'FROM_END'],\n ['first', 'FIRST'],\n ['last', 'LAST'],\n ['random', 'RANDOM'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/List/lists_getSublist.js",
"totalCharacters": 261,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['get sub-list from #', 'FROM_START'],\n ['get sub-list from # from end', 'FROM_END'],\n ['get sub-list from first', 'FIRST'],\n ['#', 'FROM_START'],\n ['# from end', 'FROM_END'],\n ['last', 'LAST'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/List/lists_indexOf.js",
"totalCharacters": 85,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['first', 'FIRST'],\n ['last', 'LAST'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/List/lists_setIndex.js",
"totalCharacters": 233,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['set', 'SET'],\n ['insert at', 'INSERT'],\n ['#', 'FROM_START'],\n ['# from end', 'FROM_END'],\n ['first', 'FIRST'],\n ['last', 'LAST'],\n ['random', 'RANDOM'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/List/lists_sort.js",
"totalCharacters": 185,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['numeric', 'NUMERIC'],\n ['alphabetic', 'TEXT'],\n ['ascending', '1'],\n ['descending', '-1'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/List/lists_split.js",
"totalCharacters": 98,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['make list from text', 'SPLIT'],\n ['make text from list', 'JOIN'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/Loops/controls_flow_statements.js",
"totalCharacters": 117,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['break out', 'BREAK'],\n ['continue with next iteration', 'CONTINUE'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Advanced/Loops/controls_whileUntil.js",
"totalCharacters": 87,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['while', 'WHILE'],\n ['until', 'UNTIL'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Binary/Tools/Misc/console.js",
"totalCharacters": 169,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['Log', 'log'],\n ['Warn', 'warn'],\n ['Error', 'error'],\n ['Table', 'table'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Binary/Trade Definition/trade_definition_contracttype.js",
"totalCharacters": 65,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " contract_type_options.push(['Both', 'both']);"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Logic/logic_boolean.js",
"totalCharacters": 85,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['true', 'TRUE'],\n ['false', 'FALSE'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Logic/logic_operation.js",
"totalCharacters": 77,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['and', 'AND'],\n ['or', 'OR'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Math/math_number_property.js",
"totalCharacters": 327,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['even', 'EVEN'],\n ['odd', 'ODD'],\n ['prime', 'PRIME'],\n ['whole', 'WHOLE'],\n ['positive', 'POSITIVE'],\n ['negative', 'NEGATIVE'],\n ['divisible by', 'DIVISIBLE_BY'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Math/math_on_list.js",
"totalCharacters": 415,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['sum', 'SUM'],\n ['min', 'MIN'],\n ['max', 'MAX'],\n ['average', 'AVERAGE'],\n ['median', 'MEDIAN'],\n ['mode', 'MODE'],\n ['antimode', 'ANTIMODE'],\n ['standard deviation', 'STD_DEV'],\n ['random item', 'RANDOM'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Math/math_single.js",
"totalCharacters": 93,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['square root', 'ROOT'],\n ['absolute', 'ABS'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Text/text_changeCase.js",
"totalCharacters": 158,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['UPPER CASE', 'UPPERCASE'],\n ['lower case', 'LOWERCASE'],\n ['Title Case', 'TITLECASE'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Text/text_charAt.js",
"totalCharacters": 263,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['letter #', 'FROM_START'],\n ['letter # from end', 'FROM_END'],\n ['first letter', 'FIRST'],\n ['last letter', 'LAST'],\n ['random letter', 'RANDOM'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Text/text_getSubstring.js",
"totalCharacters": 235,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['letter #', 'FROM_START'],\n ['letter # from end', 'FROM_END'],\n ['first', 'FIRST'],\n ['letter #', 'FROM_START'],\n ['letter # from end', 'FROM_END'],\n ['last', 'LAST'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Text/text_indexOf.js",
"totalCharacters": 85,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['first', 'FIRST'],\n ['last', 'LAST'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Text/text_prompt_ext.js",
"totalCharacters": 89,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['string', 'TEXT'],\n ['number', 'NUMBER'],"
},
{
"name": "src/external/bot-skeleton/scratch/blocks/Text/text_trim.js",
"totalCharacters": 143,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " ['both sides', 'BOTH'],\n ['left side', 'LEFT'],\n ['right side', 'RIGHT'],"
},
{
"name": "src/external/bot-skeleton/services/api/active-symbols.js",
"totalCharacters": 303,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " const market_display_name = display_name + (this.isMarketClosed(market_name) ? ' (Closed)' : '');\n display_name + (this.isSubmarketClosed(submarket_name) ? ' (Closed)' : '');\n display_name + (this.isSymbolClosed(symbol_name) ? ' (Closed)' : '');"
},
{
"name": "src/external/bot-skeleton/services/api/api-base.ts",
"totalCharacters": 959,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { handleBackendError, isBackendError } from '@/utils/error-handler';\n const errorMessage = isBackendError(response.error)\n ? handleBackendError(response.error)\n : response.error.message || 'Token exchange failed';\n console.error('Token exchange failed:', errorMessage);\n globalObserver.emit('InvalidToken', {\n error: { ...response.error, localizedMessage: errorMessag..."
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/trade/Proposal.js",
"totalCharacters": 241,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n throw Error(getLocalizedErrorMessage('ProposalsNotReady'));\n throw new Error(getLocalizedErrorMessage('SelectedProposalNotExist'));"
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/trade/Purchase.js",
"totalCharacters": 79,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " log(LogTypes.PURCHASE, { transaction_id: buy.transaction_id });"
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/trade/Sell.js",
"totalCharacters": 172,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n message: getLocalizedErrorMessage(error.code, error.details),"
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/trade/Ticks.js",
"totalCharacters": 360,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n const localizedError = {\n ...e,\n message: getLocalizedErrorMessage(e.code, e.details),\n };\n globalObserver.emit('Error', localizedError);"
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/trade/Total.js",
"totalCharacters": 292,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n throw createError('CustomLimitsReached', getLocalizedErrorMessage('MaxTradesReached'));\n throw createError('CustomLimitsReached', getLocalizedErrorMessage('MaxLossReached'));"
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/trade/index.js",
"totalCharacters": 171,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n throw createError('NotInitialized', getLocalizedErrorMessage('NotInitialized'));"
},
{
"name": "src/external/bot-skeleton/services/tradeEngine/utils/helpers.js",
"totalCharacters": 1098,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n const error_details = {\n message_type: error.msg_type,\n delay: next_delay_in_seconds,\n request: echo_req?.req_id,\n message: message || localize('The market is closed'),\n trade_type: TRADE_TYPE_CATEGORY_NAMES?.[selected_trade_type] ?? '',\n };\n\n message_to_print = getLocalizedErrorMessage('RateLimit', error_details);\n mes..."
},
{
"name": "src/hooks/api/account/useActiveAccount.tsx",
"totalCharacters": 72,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " currencyLabel: isVirtual ? 'Demo' : activeAccount?.currency,"
},
{
"name": "src/pages/bot-builder/quick-strategy/quick-strategy.scss",
"totalCharacters": 782,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " inset-inline-end: 6rem;\n inset-inline-end: 5rem;\n inset-inline-start: 1.4rem;\n inset-inline-start: 0;\n margin-inline-start: 4rem;\n margin-inline-end: 4rem;\n margin-inline-end: 2.4rem;\n margin-inline-end: 2.4rem;\n margin-inline-start: 4rem;\n margin-inline-end: 4rem;\n inset-inline-start: 1.6rem;\n margin-inli..."
},
{
"name": "src/pages/dashboard/cards.tsx",
"totalCharacters": 66,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " content: <Localize i18n_default_text='Bot Builder' />,"
},
{
"name": "src/pages/main/main.scss",
"totalCharacters": 28,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " margin-bottom: auto;"
},
{
"name": "src/pages/tutorials/dbot-tours/tour-content.tsx",
"totalCharacters": 305,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " placement: isDbotRTL() ? 'left' : 'right',\n placement: isDbotRTL() ? 'left' : 'right',\n placement: isDbotRTL() ? 'left' : 'right',\n placement: isDbotRTL() ? 'left' : 'right',\n placement: isDbotRTL() ? 'left' : 'right',\n placement: isDbotRTL() ? 'left' : 'right',"
},
{
"name": "src/pages/tutorials/tutorials.scss",
"totalCharacters": 112,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": " margin-inline-end: 0.8rem;\n margin-bottom: 0.8rem;\n margin-inline-end: 0.8rem;"
},
{
"name": "src/stores/run-panel-store.ts",
"totalCharacters": 325,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { handleBackendError, isBackendError } from '@/utils/error-handler';\n // Use localized error message if it's a backend error, otherwise fallback to original message\n let error_message = error?.message;\n if (isBackendError(error)) {\n error_message = handleBackendError(error);\n }\n"
},
{
"name": "src/utils/__tests__/error-handler.spec.ts",
"totalCharacters": 3101,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { handleBackendError, isBackendError } from '../error-handler';\n\n// Mock the backend error messages\njest.mock('../../constants/backend-error-messages', () => ({\n getLocalizedErrorMessage: jest.fn((errorCode, details) => {\n if (errorCode === 'InsufficientBalance') {\n return 'Your account balance is insufficient to buy this contract.';\n }\n if (errorCode === 'RateLimit' && details) {\n return `You are rate limited for: ${details.message_type}, ret..."
},
{
"name": "src/utils/error-handler.ts",
"totalCharacters": 3414,
"aiCharacters": 0,
"percentage": 0,
"status": "modified",
"hasAddedContent": true,
"addedContent": "import { getLocalizedErrorMessage } from '@/constants/backend-error-messages';\n\n/**\n * Interface for backend error response\n */\nexport interface BackendError {\n code: string;\n message?: string;\n details?: Record<string, any>;\n}\n\n/**\n * Interface for API error response\n */\nexport interface ApiErrorResponse {\n error?: BackendError;\n msg_type?: string;\n}\n\n/**\n * Handles backend errors and returns localized error messages\n * @param error - The error object from backend\n * @returns Loc..."
},
{
"name": "src/utils/languages.tsx",
"totalCharacters": 208,
"aiCharacters": 200,
"percentage": 96,
"status": "modified",
"hasAddedContent": true,
"addedContent": "\n// Filtered languages for the language switcher (only show specific languages)\nexport const FILTERED_LANGUAGES = LANGUAGES.filter(lang =>\n ['EN', 'ES', 'FR', 'PT', 'AR', 'IT', 'RU'].includes(lang.code)\n);"
}
]
}🚀 Analysis powered by ShiftAI • Analyzes only PR changes, not entire files |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Manifest Files |
|
Claude finished @rupato-deriv's task —— View job Code Review Complete ✅I've thoroughly reviewed the language switcher implementation across all 5 files. Here's my comprehensive feedback: Tasks:
Review Findings✅ Strengths
🔍 Areas for Improvement1. Hard-coded Language List Duplication (
|
Deploying derivatives-bot with
|
| Latest commit: |
bc1bfc8
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://37477a4b.derivatives-bot.pages.dev |
| Branch Preview URL: | https://rupato-enable--language-swit.derivatives-bot.pages.dev |
|
Claude finished @rupato-deriv's task —— View job Updated Code Review - Language Switcher Implementation ✅I've thoroughly analyzed the current language switcher implementation. The code has been significantly improved since the previous review and addresses most of the concerns I raised earlier. Tasks:
🎉 Major Improvements MadeThe implementation now successfully addresses the key issues from my previous review: ✅ Fixed Issues
🔍 Current Code Quality AssessmentArchitecture (9/10)
Error Handling (9/10)
User Experience (8/10)
Code Maintainability (9/10)
🔧 Minor Remaining Observations1. Page Reload Strategy (
|
…pato/Enable--language-switcher-for-dbot
|
Claude encountered an error —— View job Code Review in Progress
|
|
Claude finished @rupato-deriv's task —— View job Code Review Complete ✅I've thoroughly reviewed this PR focusing on correctness, code quality, performance, security, and test coverage. Here are my findings: SummaryThis PR adds comprehensive language switcher functionality and backend error localization to the dbot application. The implementation is generally solid with good test coverage for new error handling logic, but there are several important issues to address. Overall Assessment: 7.5/10 - Good implementation with some critical security and performance concerns that should be addressed before merge. 🔴 High Priority Issues1. XSS Vulnerability in Error Messages (Security)Location: export const getLocalizedErrorMessage = (errorCode: string, details?: Record<string, any>): string => {
// ...
const processedParams = processBackendParameters(message, details);
return localize(message, processedParams);
};Issue: User-supplied data from Impact: Potential XSS attack vector if error details contain user-controlled data. Fix: Sanitize parameters before interpolation: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
const sanitizeValue = (value: any): any => {
if (typeof value === 'string') {
// Basic HTML escaping
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
return value;
};
// Handle numbered parameters
if (details._1 !== undefined) params.param1 = sanitizeValue(details._1);
// ... rest of parameters
// Handle named parameters
Object.keys(details).forEach(key => {
if (!key.startsWith('_')) {
params[key] = sanitizeValue(details[key]);
}
});
return params;
};2. Page Reload Pattern May Break State (Correctness)Location: window.location.replace(getActiveTabUrl());
window.location.reload();Issues:
Impact: May cause unexpected behavior, especially if Recommendation: Use only ONE navigation method: // Option 1: Use only replace (recommended)
window.location.replace(getActiveTabUrl());
// Option 2: If full reload is truly needed
window.location.reload();Question: Why is 3. Loose Equality Operator (Code Quality)Location: .find(({ code }: { code: string }) => code == currentLang)Issue: Uses loose equality Fix: .find(({ code }: { code: string }) => code === currentLang)🟡 Medium Priority Issues4. Language URL Parameter Not Validated (Security)Location: const langCode = langParam.toUpperCase() as (typeof FILTERED_LANGUAGES)[number]['code'];Issue: Type assertion doesn't provide runtime validation. Malicious values could be forced through. Fix: Add runtime validation: const langCode = langParam.toUpperCase();
const supportedLangCodes = FILTERED_LANGUAGES.map(lang => lang.code);
if (supportedLangCodes.includes(langCode)) {
const validatedLangCode = langCode as (typeof FILTERED_LANGUAGES)[number]['code'];
try {
switchLanguage(validatedLangCode);
// ... rest
} catch (error) {
console.error('Failed to switch language:', error);
}
}Current code already does this check at line 40, so the type assertion at line 36 is premature and misleading. 5. Unnecessary useMemo Recalculation (Performance)Location: const countryIcon = useMemo(
() => FILTERED_LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon,
[currentLang]
);Issue: The Optimization: Create a lookup map once: // At module level or in a constants file
const LANGUAGE_ICONS = FILTERED_LANGUAGES.reduce((acc, lang) => {
acc[lang.code] = {
placeholder: lang.placeholderIcon,
placeholderMobile: lang.placeholderIconInMobile,
};
return acc;
}, {} as Record<string, any>);
// In component
const countryIcon = LANGUAGE_ICONS[currentLang]?.placeholder;6. Missing Null Checks in Error Handler (Robustness)Location: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
return getLocalizedErrorMessage('GeneralError');
}
return getLocalizedErrorMessage(error.code, error.details);
};Issue: Doesn't validate that Impact: If an unknown error code is passed, it returns a generic message (which is fine), but there's no logging or telemetry for unknown error codes. Recommendation: Add logging for unknown error codes: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
console.warn('Backend error missing error code:', error);
return getLocalizedErrorMessage('GeneralError');
}
const errorMessages = getBackendErrorMessages();
if (!errorMessages[error.code as keyof typeof errorMessages]) {
console.warn('Unknown backend error code:', error.code, error);
}
return getLocalizedErrorMessage(error.code, error.details);
};7. Test Mock Leakage (Tests)Location: jest.mock('@deriv-com/translations', () => ({
localize: jest.fn((message, params) => { /* ... */ }),
}));Issue: Mocks defined at module level affect all tests in the file. If tests run in different orders or in parallel, this could cause issues. Best Practice: Use beforeEach(() => {
jest.clearAllMocks();
});🟢 Low Priority / Nice to Have8. Blockly Block Definition Changes (Code Quality)Locations: Multiple files in Observation: Many Blockly block definitions were changed from hardcoded English strings to English strings (appears to be preparatory work for future localization). Examples:
Impact: None currently, but these files are now "touched" in the PR which makes review harder. Question: Are these changes necessary for this PR, or are they preparatory work for future localization? If preparatory, consider a separate PR to reduce review complexity. 9. Inconsistent Error Handling Patterns (Code Quality)Location: Multiple files Some files use: try {
switchLanguage(code);
hideModal();
window.location.replace(getActiveTabUrl());
window.location.reload();
} catch (error) {
console.error('Failed to switch language:', error);
hideModal();
}While others just: switchLanguage(code);Recommendation: Standardize error handling across all language switching operations. 10. Type Safety Could Be Improved (Code Quality)Location: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
// ...
}Issue: Using Improvement: interface BackendErrorDetails {
_1?: string | number;
_2?: string | number;
_3?: string | number;
_4?: string | number;
_5?: string | number;
[key: string]: string | number | undefined;
}
const processBackendParameters = (message: string, details?: BackendErrorDetails) => {
// ...
}✅ Positive Aspects
📋 Testing RecommendationsManual Testing Checklist:
Unit Test Gaps:
🎯 Recommended Action ItemsBefore Merge (High Priority):
After Merge (Medium Priority):
Future Improvements (Low Priority):
📊 Code Metrics
|
|
Claude finished @rupato-deriv's task —— View job Code Review Complete ✅I've thoroughly reviewed this PR focusing on correctness, code quality, performance, security, and test coverage. Here are my findings: SummaryThis PR adds comprehensive language switcher functionality and backend error localization to the dbot application. The implementation is generally solid with good test coverage for new error handling logic, but there are several important issues to address. Overall Assessment: 7.5/10 - Good implementation with some critical security and performance concerns that should be addressed before merge. 🔴 High Priority Issues1. XSS Vulnerability in Error Messages (Security)Location: export const getLocalizedErrorMessage = (errorCode: string, details?: Record<string, any>): string => {
// ...
const processedParams = processBackendParameters(message, details);
return localize(message, processedParams);
};Issue: User-supplied data from Impact: Potential XSS attack vector if error details contain user-controlled data. Fix: Sanitize parameters before interpolation: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
const sanitizeValue = (value: any): any => {
if (typeof value === 'string') {
// Basic HTML escaping
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
return value;
};
// Handle numbered parameters
if (details._1 !== undefined) params.param1 = sanitizeValue(details._1);
// ... rest of parameters
// Handle named parameters
Object.keys(details).forEach(key => {
if (!key.startsWith('_')) {
params[key] = sanitizeValue(details[key]);
}
});
return params;
};2. Page Reload Pattern May Break State (Correctness)Location: window.location.replace(getActiveTabUrl());
window.location.reload();Issues:
Impact: May cause unexpected behavior, especially if Recommendation: Use only ONE navigation method: // Option 1: Use only replace (recommended)
window.location.replace(getActiveTabUrl());
// Option 2: If full reload is truly needed
window.location.reload();Question: Why is 3. Loose Equality Operator (Code Quality)Location: .find(({ code }: { code: string }) => code == currentLang)Issue: Uses loose equality Fix: .find(({ code }: { code: string }) => code === currentLang)🟡 Medium Priority Issues4. Language URL Parameter Not Validated (Security)Location: const langCode = langParam.toUpperCase() as (typeof FILTERED_LANGUAGES)[number]['code'];Issue: Type assertion doesn't provide runtime validation. Malicious values could be forced through. Fix: Add runtime validation: const langCode = langParam.toUpperCase();
const supportedLangCodes = FILTERED_LANGUAGES.map(lang => lang.code);
if (supportedLangCodes.includes(langCode)) {
const validatedLangCode = langCode as (typeof FILTERED_LANGUAGES)[number]['code'];
try {
switchLanguage(validatedLangCode);
// ... rest
} catch (error) {
console.error('Failed to switch language:', error);
}
}Current code already does this check at line 40, so the type assertion at line 36 is premature and misleading. 5. Unnecessary useMemo Recalculation (Performance)Location: const countryIcon = useMemo(
() => FILTERED_LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon,
[currentLang]
);Issue: The Optimization: Create a lookup map once: // At module level or in a constants file
const LANGUAGE_ICONS = FILTERED_LANGUAGES.reduce((acc, lang) => {
acc[lang.code] = {
placeholder: lang.placeholderIcon,
placeholderMobile: lang.placeholderIconInMobile,
};
return acc;
}, {} as Record<string, any>);
// In component
const countryIcon = LANGUAGE_ICONS[currentLang]?.placeholder;6. Missing Null Checks in Error Handler (Robustness)Location: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
return getLocalizedErrorMessage('GeneralError');
}
return getLocalizedErrorMessage(error.code, error.details);
};Issue: Doesn't validate that Impact: If an unknown error code is passed, it returns a generic message (which is fine), but there's no logging or telemetry for unknown error codes. Recommendation: Add logging for unknown error codes: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
console.warn('Backend error missing error code:', error);
return getLocalizedErrorMessage('GeneralError');
}
const errorMessages = getBackendErrorMessages();
if (!errorMessages[error.code as keyof typeof errorMessages]) {
console.warn('Unknown backend error code:', error.code, error);
}
return getLocalizedErrorMessage(error.code, error.details);
};7. Test Mock Leakage (Tests)Location: jest.mock('@deriv-com/translations', () => ({
localize: jest.fn((message, params) => { /* ... */ }),
}));Issue: Mocks defined at module level affect all tests in the file. If tests run in different orders or in parallel, this could cause issues. Best Practice: Use beforeEach(() => {
jest.clearAllMocks();
});🟢 Low Priority / Nice to Have8. Blockly Block Definition Changes (Code Quality)Locations: Multiple files in Observation: Many Blockly block definitions were changed from hardcoded English strings to English strings (appears to be preparatory work for future localization). Examples:
Impact: None currently, but these files are now "touched" in the PR which makes review harder. Question: Are these changes necessary for this PR, or are they preparatory work for future localization? If preparatory, consider a separate PR to reduce review complexity. 9. Inconsistent Error Handling Patterns (Code Quality)Location: Multiple files Some files use: try {
switchLanguage(code);
hideModal();
window.location.replace(getActiveTabUrl());
window.location.reload();
} catch (error) {
console.error('Failed to switch language:', error);
hideModal();
}While others just: switchLanguage(code);Recommendation: Standardize error handling across all language switching operations. 10. Type Safety Could Be Improved (Code Quality)Location: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
// ...
}Issue: Using Improvement: interface BackendErrorDetails {
_1?: string | number;
_2?: string | number;
_3?: string | number;
_4?: string | number;
_5?: string | number;
[key: string]: string | number | undefined;
}
const processBackendParameters = (message: string, details?: BackendErrorDetails) => {
// ...
}✅ Positive Aspects
📋 Testing RecommendationsManual Testing Checklist:
Unit Test Gaps:
🎯 Recommended Action ItemsBefore Merge (High Priority):
After Merge (Medium Priority):
Future Improvements (Low Priority):
📊 Code Metrics
|
|
Claude finished @rupato-deriv's task —— View job Code Review Complete ✅I've thoroughly reviewed this PR focusing on correctness, code quality, performance, security, and test coverage. Here are my findings: SummaryThis PR adds comprehensive language switcher functionality and backend error localization to the dbot application. The implementation is generally solid with good test coverage for new error handling logic, but there are several important issues to address. Overall Assessment: 7.5/10 - Good implementation with some critical security and performance concerns that should be addressed before merge. 🔴 High Priority Issues1. XSS Vulnerability in Error Messages (Security)Location: export const getLocalizedErrorMessage = (errorCode: string, details?: Record<string, any>): string => {
// ...
const processedParams = processBackendParameters(message, details);
return localize(message, processedParams);
};Issue: User-supplied data from Impact: Potential XSS attack vector if error details contain user-controlled data. Fix: Sanitize parameters before interpolation: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
const sanitizeValue = (value: any): any => {
if (typeof value === 'string') {
// Basic HTML escaping
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
return value;
};
// Handle numbered parameters
if (details._1 !== undefined) params.param1 = sanitizeValue(details._1);
// ... rest of parameters
// Handle named parameters
Object.keys(details).forEach(key => {
if (!key.startsWith('_')) {
params[key] = sanitizeValue(details[key]);
}
});
return params;
};2. Page Reload Pattern May Break State (Correctness)Location: window.location.replace(getActiveTabUrl());
window.location.reload();Issues:
Impact: May cause unexpected behavior, especially if Recommendation: Use only ONE navigation method: // Option 1: Use only replace (recommended)
window.location.replace(getActiveTabUrl());
// Option 2: If full reload is truly needed
window.location.reload();Question: Why is 3. Loose Equality Operator (Code Quality)Location: .find(({ code }: { code: string }) => code == currentLang)Issue: Uses loose equality Fix: .find(({ code }: { code: string }) => code === currentLang)🟡 Medium Priority Issues4. Language URL Parameter Not Validated (Security)Location: const langCode = langParam.toUpperCase() as (typeof FILTERED_LANGUAGES)[number]['code'];Issue: Type assertion doesn't provide runtime validation. Malicious values could be forced through. Fix: Add runtime validation: const langCode = langParam.toUpperCase();
const supportedLangCodes = FILTERED_LANGUAGES.map(lang => lang.code);
if (supportedLangCodes.includes(langCode)) {
const validatedLangCode = langCode as (typeof FILTERED_LANGUAGES)[number]['code'];
try {
switchLanguage(validatedLangCode);
// ... rest
} catch (error) {
console.error('Failed to switch language:', error);
}
}Current code already does this check at line 40, so the type assertion at line 36 is premature and misleading. 5. Unnecessary useMemo Recalculation (Performance)Location: const countryIcon = useMemo(
() => FILTERED_LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon,
[currentLang]
);Issue: The Optimization: Create a lookup map once: // At module level or in a constants file
const LANGUAGE_ICONS = FILTERED_LANGUAGES.reduce((acc, lang) => {
acc[lang.code] = {
placeholder: lang.placeholderIcon,
placeholderMobile: lang.placeholderIconInMobile,
};
return acc;
}, {} as Record<string, any>);
// In component
const countryIcon = LANGUAGE_ICONS[currentLang]?.placeholder;6. Missing Null Checks in Error Handler (Robustness)Location: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
return getLocalizedErrorMessage('GeneralError');
}
return getLocalizedErrorMessage(error.code, error.details);
};Issue: Doesn't validate that Impact: If an unknown error code is passed, it returns a generic message (which is fine), but there's no logging or telemetry for unknown error codes. Recommendation: Add logging for unknown error codes: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
console.warn('Backend error missing error code:', error);
return getLocalizedErrorMessage('GeneralError');
}
const errorMessages = getBackendErrorMessages();
if (!errorMessages[error.code as keyof typeof errorMessages]) {
console.warn('Unknown backend error code:', error.code, error);
}
return getLocalizedErrorMessage(error.code, error.details);
};7. Test Mock Leakage (Tests)Location: jest.mock('@deriv-com/translations', () => ({
localize: jest.fn((message, params) => { /* ... */ }),
}));Issue: Mocks defined at module level affect all tests in the file. If tests run in different orders or in parallel, this could cause issues. Best Practice: Use beforeEach(() => {
jest.clearAllMocks();
});🟢 Low Priority / Nice to Have8. Blockly Block Definition Changes (Code Quality)Locations: Multiple files in Observation: Many Blockly block definitions were changed from hardcoded English strings to English strings (appears to be preparatory work for future localization). Examples:
Impact: None currently, but these files are now "touched" in the PR which makes review harder. Question: Are these changes necessary for this PR, or are they preparatory work for future localization? If preparatory, consider a separate PR to reduce review complexity. 9. Inconsistent Error Handling Patterns (Code Quality)Location: Multiple files Some files use: try {
switchLanguage(code);
hideModal();
window.location.replace(getActiveTabUrl());
window.location.reload();
} catch (error) {
console.error('Failed to switch language:', error);
hideModal();
}While others just: switchLanguage(code);Recommendation: Standardize error handling across all language switching operations. 10. Type Safety Could Be Improved (Code Quality)Location: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
// ...
}Issue: Using Improvement: interface BackendErrorDetails {
_1?: string | number;
_2?: string | number;
_3?: string | number;
_4?: string | number;
_5?: string | number;
[key: string]: string | number | undefined;
}
const processBackendParameters = (message: string, details?: BackendErrorDetails) => {
// ...
}✅ Positive Aspects
📋 Testing RecommendationsManual Testing Checklist:
Unit Test Gaps:
🎯 Recommended Action ItemsBefore Merge (High Priority):
After Merge (Medium Priority):
Future Improvements (Low Priority):
📊 Code Metrics
|
|
Claude finished @rupato-deriv's task —— View job Code Review Complete ✅I've thoroughly reviewed this PR focusing on correctness, code quality, performance, security, and test coverage. Here are my findings: SummaryThis PR adds comprehensive language switcher functionality and backend error localization to the dbot application. The implementation is generally solid with good test coverage for new error handling logic, but there are several important issues to address. Overall Assessment: 7.5/10 - Good implementation with some critical security and performance concerns that should be addressed before merge. 🔴 High Priority Issues1. XSS Vulnerability in Error Messages (Security)Location: export const getLocalizedErrorMessage = (errorCode: string, details?: Record<string, any>): string => {
// ...
const processedParams = processBackendParameters(message, details);
return localize(message, processedParams);
};Issue: User-supplied data from Impact: Potential XSS attack vector if error details contain user-controlled data. Fix: Sanitize parameters before interpolation: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
const sanitizeValue = (value: any): any => {
if (typeof value === 'string') {
// Basic HTML escaping
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
return value;
};
// Handle numbered parameters
if (details._1 !== undefined) params.param1 = sanitizeValue(details._1);
// ... rest of parameters
// Handle named parameters
Object.keys(details).forEach(key => {
if (!key.startsWith('_')) {
params[key] = sanitizeValue(details[key]);
}
});
return params;
};2. Page Reload Pattern May Break State (Correctness)Location: window.location.replace(getActiveTabUrl());
window.location.reload();Issues:
Impact: May cause unexpected behavior, especially if Recommendation: Use only ONE navigation method: // Option 1: Use only replace (recommended)
window.location.replace(getActiveTabUrl());
// Option 2: If full reload is truly needed
window.location.reload();Question: Why is 3. Loose Equality Operator (Code Quality)Location: .find(({ code }: { code: string }) => code == currentLang)Issue: Uses loose equality Fix: .find(({ code }: { code: string }) => code === currentLang)🟡 Medium Priority Issues4. Language URL Parameter Not Validated (Security)Location: const langCode = langParam.toUpperCase() as (typeof FILTERED_LANGUAGES)[number]['code'];Issue: Type assertion doesn't provide runtime validation. Malicious values could be forced through. Fix: Add runtime validation: const langCode = langParam.toUpperCase();
const supportedLangCodes = FILTERED_LANGUAGES.map(lang => lang.code);
if (supportedLangCodes.includes(langCode)) {
const validatedLangCode = langCode as (typeof FILTERED_LANGUAGES)[number]['code'];
try {
switchLanguage(validatedLangCode);
// ... rest
} catch (error) {
console.error('Failed to switch language:', error);
}
}Current code already does this check at line 40, so the type assertion at line 36 is premature and misleading. 5. Unnecessary useMemo Recalculation (Performance)Location: const countryIcon = useMemo(
() => FILTERED_LANGUAGES.find(({ code }: { code: string }) => code == currentLang)?.placeholderIcon,
[currentLang]
);Issue: The Optimization: Create a lookup map once: // At module level or in a constants file
const LANGUAGE_ICONS = FILTERED_LANGUAGES.reduce((acc, lang) => {
acc[lang.code] = {
placeholder: lang.placeholderIcon,
placeholderMobile: lang.placeholderIconInMobile,
};
return acc;
}, {} as Record<string, any>);
// In component
const countryIcon = LANGUAGE_ICONS[currentLang]?.placeholder;6. Missing Null Checks in Error Handler (Robustness)Location: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
return getLocalizedErrorMessage('GeneralError');
}
return getLocalizedErrorMessage(error.code, error.details);
};Issue: Doesn't validate that Impact: If an unknown error code is passed, it returns a generic message (which is fine), but there's no logging or telemetry for unknown error codes. Recommendation: Add logging for unknown error codes: export const handleBackendError = (error: BackendError): string => {
if (!error?.code) {
console.warn('Backend error missing error code:', error);
return getLocalizedErrorMessage('GeneralError');
}
const errorMessages = getBackendErrorMessages();
if (!errorMessages[error.code as keyof typeof errorMessages]) {
console.warn('Unknown backend error code:', error.code, error);
}
return getLocalizedErrorMessage(error.code, error.details);
};7. Test Mock Leakage (Tests)Location: jest.mock('@deriv-com/translations', () => ({
localize: jest.fn((message, params) => { /* ... */ }),
}));Issue: Mocks defined at module level affect all tests in the file. If tests run in different orders or in parallel, this could cause issues. Best Practice: Use beforeEach(() => {
jest.clearAllMocks();
});🟢 Low Priority / Nice to Have8. Blockly Block Definition Changes (Code Quality)Locations: Multiple files in Observation: Many Blockly block definitions were changed from hardcoded English strings to English strings (appears to be preparatory work for future localization). Examples:
Impact: None currently, but these files are now "touched" in the PR which makes review harder. Question: Are these changes necessary for this PR, or are they preparatory work for future localization? If preparatory, consider a separate PR to reduce review complexity. 9. Inconsistent Error Handling Patterns (Code Quality)Location: Multiple files Some files use: try {
switchLanguage(code);
hideModal();
window.location.replace(getActiveTabUrl());
window.location.reload();
} catch (error) {
console.error('Failed to switch language:', error);
hideModal();
}While others just: switchLanguage(code);Recommendation: Standardize error handling across all language switching operations. 10. Type Safety Could Be Improved (Code Quality)Location: const processBackendParameters = (message: string, details?: Record<string, any>) => {
if (!details) return {};
const params: Record<string, any> = {};
// ...
}Issue: Using Improvement: interface BackendErrorDetails {
_1?: string | number;
_2?: string | number;
_3?: string | number;
_4?: string | number;
_5?: string | number;
[key: string]: string | number | undefined;
}
const processBackendParameters = (message: string, details?: BackendErrorDetails) => {
// ...
}✅ Positive Aspects
📋 Testing RecommendationsManual Testing Checklist:
Unit Test Gaps:
🎯 Recommended Action ItemsBefore Merge (High Priority):
After Merge (Medium Priority):
Future Improvements (Low Priority):
📊 Code Metrics
|

🤖 Auto-generated PR
This PR was automatically created by ShiftAI CLI.
fix: Rupato/Enable--language-switcher-on-dbot