Skip to content

Conversation

@akmal-deriv
Copy link
Collaborator

🤖 Auto-generated PR

This PR was automatically created by ShiftAI CLI.


refactor: restore language switcher functionality in menu title and footer

@akmal-deriv
Copy link
Collaborator Author

akmal-deriv commented Oct 14, 2025

🤖 AI Code Analysis Results

📝 Manual Coding

⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%

0 of 4,545 characters (0%) in newly added lines are AI-generated


📊 Analysis Summary

Metric Value
📁 Files Changed 12
New Lines Added 4,545 chars
🤖 AI Characters 0
📈 AI Percentage 0%

📋 Per-File Breakdown (New Lines Only)

File Lines Added AI Content Percentage
.../Components/ToggleMenu/menu-title.tsx 191 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...tainers/Layout/trading-hub-footer.jsx 242 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...rc/utils/constants/default-options.ts 248 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...ation/declarative-validation-rules.ts 38 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...ared/src/utils/validator/validator.ts 166 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...s/__tests__/contract-details.spec.tsx 217 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
.../ContractDetails/contract-details.tsx 68 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...onents/__tests__/top-widgets.spec.tsx 404 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...SmartChart/Components/top-widgets.tsx 29 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...ts/__tests__/validation-rules.spec.ts 996 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...Trading/Constants/validation-rules.ts 1754 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%
...Stores/Modules/Trading/trade-store.ts 192 chars 0 chars ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0%

📝 This PR contains traditional hand-written code.

📊 Raw Data (for dashboard)
{
  "timestamp": "2025-10-16T07:30:05.700Z",
  "repository": "deriv-com/derivatives-trader",
  "branch": "unknown",
  "author": "unknown",
  "summary": {
    "totalCharacters": 4545,
    "aiCharacters": 0,
    "percentage": 0
  },
  "files": [
    {
      "name": "packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx",
      "totalCharacters": 191,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "    const { common, ui } = useStore();\n    const { current_language } = common;\n    const { is_mobile_language_menu_open, setMobileLanguageMenuOpen } = ui;\n            <div\n            </div>"
    },
    {
      "name": "packages/core/src/App/Containers/Layout/trading-hub-footer.jsx",
      "totalCharacters": 242,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "    ToggleLanguageSettings,\n    const { common, ui } = useStore();\n    const { current_language } = common;\n        is_language_settings_modal_on,\n        toggleLanguageSettingsModal,\n                <ToggleLanguageSettings\n                />"
    },
    {
      "name": "packages/shared/src/utils/constants/default-options.ts",
      "totalCharacters": 248,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": " * Supported languages: EN, ES, FR, PT, AR, IT, RU\nexport const UNSUPPORTED_LANGUAGES = [\n    'BN',\n    'DE',\n    'ID',\n    'KM',\n    'KO',\n    'MN',\n    'PL',\n    'SI',\n    'SW',\n    'TR',\n    'UZ',\n    'VI',\n    'ZH_CN',\n    'ZH_TW',\n    'TH',\n];"
    },
    {
      "name": "packages/shared/src/utils/validation/declarative-validation-rules.ts",
      "totalCharacters": 38,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "    message?: string | (() => string);"
    },
    {
      "name": "packages/shared/src/utils/validator/validator.ts",
      "totalCharacters": 166,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "            (typeof rule.options.message === 'function' ? rule.options.message() : rule.options.message) ||\n                    ? (rule.options.min?.toString() ?? '')"
    },
    {
      "name": "packages/trader/src/AppV2/Containers/ContractDetails/__tests__/contract-details.spec.tsx",
      "totalCharacters": 217,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "    // it('should render the PayoutInfo component', async () => {\n    //     await waitFor(() => renderContractDetails());\n    //     expect(screen.getByText('Payout Info Placeholder')).toBeInTheDocument();\n    // });"
    },
    {
      "name": "packages/trader/src/AppV2/Containers/ContractDetails/contract-details.tsx",
      "totalCharacters": 68,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "                {/* <PayoutInfo contract_info={contract_info} /> */}"
    },
    {
      "name": "packages/trader/src/Modules/SmartChart/Components/__tests__/top-widgets.spec.tsx",
      "totalCharacters": 404,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "    // it('Should render info box and mocked chart title and should not render toast notification or recent trade info for Desktop', () => {\n    //     render(<TopWidgets {...mocked_props} />);\n    //     const chart_title = screen.getByText(/mockedcharttitle/i);\n    //     expect(screen.getByText(/info box content/i)).toBeInTheDocument();\n    //     expect(chart_title).toBeInTheDocument();\n    // });"
    },
    {
      "name": "packages/trader/src/Modules/SmartChart/Components/top-widgets.tsx",
      "totalCharacters": 29,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "            {/* {InfoBox} */}"
    },
    {
      "name": "packages/trader/src/Stores/Modules/Trading/Constants/__tests__/validation-rules.spec.ts",
      "totalCharacters": 996,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "type TExtendedRuleOptions = TRuleOptions<TTradeStore> & {\n    condition: (store: TTradeStore) => boolean;\n    message: string | (() => string);\n};\n        const message = (multiplier_validation_rules.stop_loss.rules[0][1] as TExtendedRuleOptions).message;\n        expect(typeof message === 'function' ? message() : message).toBe('Please enter a stop loss amount.');\n        const message = (multiplier_validation_rules.take_profit.rules[0][1] as TExtendedRuleOptions).message;\n        expect(typeof m..."
    },
    {
      "name": "packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts",
      "totalCharacters": 1754,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "// Helper function to create dynamic validation messages that update with language changes\nconst getDynamicMessage = (messageKey: string) => () => localize(messageKey);\n\n            ['req', { message: getDynamicMessage('Amount is a required field.') }],\n                    message: getDynamicMessage('Barrier is a required field.'),\n                    message: getDynamicMessage('Higher barrier must be higher than lower barrier.'),\n                    message: getDynamicMessage('Barrier cannot be..."
    },
    {
      "name": "packages/trader/src/Stores/Modules/Trading/trade-store.ts",
      "totalCharacters": 192,
      "aiCharacters": 0,
      "percentage": 0,
      "status": "modified",
      "hasAddedContent": true,
      "addedContent": "                // Clear existing validation errors to prevent stale messages\n                this.validation_errors = {};\n\n                // Regenerate all validation rules with new language"
    }
  ]
}

🚀 Analysis powered by ShiftAI • Analyzes only PR changes, not entire files

@github-actions
Copy link
Contributor

github-actions bot commented Oct 14, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails

Scanned Manifest Files

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Oct 14, 2025

Deploying derivatives-trader with  Cloudflare Pages  Cloudflare Pages

Latest commit: 929c4e6
Status: ✅  Deploy successful!
Preview URL: https://3e2d2dc0.derivatives-trader.pages.dev
Branch Preview URL: https://f-enable-languages.derivatives-trader.pages.dev

View logs

matin-deriv
matin-deriv previously approved these changes Oct 15, 2025
@akmal-deriv akmal-deriv changed the title refactor: restore language switcher functionality in menu title and footer Akmal / refactor: restore language switcher functionality in menu title and footer Oct 15, 2025
nijil-deriv
nijil-deriv previously approved these changes Oct 15, 2025
matin-deriv
matin-deriv previously approved these changes Oct 15, 2025
- Added a new utility function `mapErrorMessage` to map error subcodes to user-friendly localized messages.
- Updated the `showDigitalOptionsUnavailableError` function to utilize the new error mapping.
- Created comprehensive tests for the `mapErrorMessage` function to ensure correct behavior for various error scenarios.
- Refactored components and hooks across the application to replace direct error message usage with the mapped messages.
- Updated types to include `subcode` and `errorcode_arr_js` for better error context.
- Ensured consistent error handling in service error modals, trading components, and proposal handling.
@claude
Copy link

claude bot commented Oct 23, 2025

Claude finished @akmal-deriv's task —— View job


Code Review Complete

I've completed a comprehensive review of PR #368. Here are my findings organized by priority:


🔴 High Priority Issues

1. Commented-Out Code Should Be Removed or Re-enabled

Files affected:

  • packages/trader/src/AppV2/Containers/ContractDetails/contract-details.tsx:105
  • packages/trader/src/Modules/SmartChart/Components/top-widgets.tsx:58
  • packages/trader/src/AppV2/Containers/ContractDetails/__tests__/contract-details.spec.tsx:215-218
  • packages/trader/src/Modules/SmartChart/Components/__tests__/top-widgets.spec.tsx (multiple tests)

Issue: Multiple components and tests are commented out without explanation:

{/* <PayoutInfo contract_info={contract_info} /> */}
{/* {InfoBox} */}

Recommendation: Either:

  • Remove the commented code if it's no longer needed
  • Re-enable it if it should be working
  • Add a TODO/FIXME comment explaining why it's temporarily disabled
  • Create a follow-up issue/ticket if work is in progress

2. Language Validation: Missing Runtime Check

File: packages/shared/src/utils/constants/default-options.ts:7-23

Issue: The UNSUPPORTED_LANGUAGES array is exported but there's no runtime validation to ensure the current language isn't in this list when switching languages.

Recommendation: Add validation in common-store.js:changeSelectedLanguage():

changeSelectedLanguage = async key => {
    if (UNSUPPORTED_LANGUAGES.includes(key)) {
        throw new Error(`Language ${key} is not supported`);
    }
    // ... rest of the code
}

3. Potential Memory Leak in Language Change Timer

File: packages/core/src/Stores/common-store.js:91-100

Issue: The changing_language_timer_id is not cleared on component unmount, which could cause memory leaks if the store is destroyed while the timer is active.

Recommendation: Add cleanup logic or convert to a proper observable reaction.


🟡 Medium Priority Issues

4. Dynamic Message Functions: Performance Consideration

File: packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts:17-18

Issue: The getDynamicMessage helper creates a new function for every validation message:

const getDynamicMessage = (messageKey: string) => () => localize(messageKey);

While this enables dynamic language updates, it creates many closures. Each rule now stores a function instead of a string.

Recommendation: This is acceptable for the use case, but consider:

  • Documenting why functions are needed (for language switching)
  • Ensuring the validator properly handles function messages (✅ already done in validator.ts:39)

5. Error Message Mapper: Missing Type Safety for Subcodes

File: packages/shared/src/utils/error-mapping/error-message-mapper.ts:26-462

Issue: The error.subcode is typed as string | undefined but the switch statement uses string literals. There's no compile-time type safety ensuring all possible subcodes are handled.

Recommendation: Create a union type for valid subcodes:

type ErrorSubcode = 
  | 'AccountBalanceExceedsLimit' 
  | 'AlreadyExpired'
  // ... etc
  ;

export const mapErrorMessage = (error: ErrorObject & { subcode?: ErrorSubcode }): string => {

6. Language Switcher: No Loading State

File: packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx:8-36

Issue: When clicking the language button, there's no visual feedback that a language switch operation is in progress. The changeSelectedLanguage is async but the UI doesn't reflect loading state.

Recommendation: Show a loading indicator while language is changing using is_language_changing from the store.

7. Inconsistent Error Handling Approach

Files affected:

  • packages/core/src/Stores/common-store.js:243-257
  • packages/shared/src/utils/error-mapping/error-message-mapper.ts

Issue: Error handling uses both mapErrorMessage() (new centralized approach) and direct localize() calls throughout the codebase. This PR partially migrates to the new system.

Recommendation:

  • Document the migration strategy
  • Create a follow-up task to migrate remaining error messages
  • Ensure all backend errors go through mapErrorMessage()

🟢 Low Priority / Suggestions

8. Test Coverage for Error Message Mapper

File: packages/shared/src/utils/error-mapping/__tests__/error-message-mapper.spec.ts

Good: Comprehensive tests for the error mapper ✅

Suggestion: Add integration tests to verify error messages actually display correctly in the UI when language changes.

9. Missing JSDoc for Public Exports

File: packages/shared/src/utils/constants/default-options.ts:7

Issue: Comment says "Supported languages: EN, ES, FR, PT, AR, IT, RU" but uses informal comment style.

Recommendation: Use JSDoc for better IDE support:

/**
 * List of language codes that are not supported in this environment.
 * Supported languages are: EN, ES, FR, PT, AR, IT, RU
 * @constant {string[]}
 */
export const UNSUPPORTED_LANGUAGES = [

10. Validation Store Clearing: Add Comment

File: packages/trader/src/Stores/Modules/Trading/trade-store.ts (around line where validation_errors are cleared)

Good: The code clears validation errors before regenerating rules ✅

Suggestion: The comment "Clear existing validation errors to prevent stale messages" is helpful. Consider adding why this is needed specifically for language changes.


✅ Things Done Well

  1. Excellent Error Mapping System: The new centralized error message mapper is well-designed with proper parameter substitution and comprehensive test coverage.

  2. Smart Dynamic Validation Messages: Using function closures for validation messages is a clever solution to the language switching problem.

  3. Backward Compatibility: The error mapper gracefully falls back to backend messages when subcodes aren't mapped.

  4. Type Safety: Good use of TypeScript interfaces for ErrorObject and proper type definitions.

  5. Proper Localization Integration: Language switching properly integrates with moment.js, localStorage, and URL parameters.


🔒 Security Review

No security issues found. Specifically checked:

  • ✅ No XSS vulnerabilities (all user input is properly sanitized through localize())
  • ✅ No secret exposure (language codes are safe to expose)
  • ✅ Auth flows not affected by changes
  • ✅ Error messages don't leak sensitive information

⚡ Performance Review

Minor Concerns:

  1. Language Change Re-renders: Changing language will cause many components to re-render. This is expected but ensure:

    • Components using localize() are properly memoized where needed
    • The 2.5-second debounce on is_language_changing helps prevent rapid re-renders ✅
  2. Validation Rule Generation: getValidationRules() is called on each validation. Since rules now contain functions, there's slightly more memory overhead, but this is acceptable.


🧪 Testing Review

Issues:

  1. Tests Commented Out: Several test files have commented tests (see High Priority build(deps-dev): bump webpack-dev-server from 5.2.0 to 5.2.1 #1)
  2. Missing E2E Tests: No end-to-end tests for language switching functionality
  3. Test Updates Needed: Validation rule tests properly updated to handle function messages ✅

Recommendations:

  • Uncomment tests or remove them
  • Add integration test for language switching UI flow
  • Test validation message updates when language changes

📋 React/TypeScript Best Practices

Good:

  • ✅ Proper use of observers with MobX
  • ✅ Type safety with TypeScript interfaces
  • ✅ Functional components with hooks

Could Improve:

  • Consider using React.memo() for components that use language-dependent strings
  • The menu-title.tsx could benefit from useMemo for the flag rendering

Summary

This PR successfully restores language switcher functionality and introduces a robust error message mapping system. The main concerns are:

  1. Must fix: Commented-out code and tests need resolution
  2. Should fix: Add runtime validation for unsupported languages
  3. Should fix: Timer cleanup in language change logic
  4. Nice to have: Better loading states and type safety for error subcodes

Overall, the implementation is solid, but the commented code needs immediate attention before merge.


@claude
Copy link

claude bot commented Oct 23, 2025

Claude finished @akmal-deriv's task —— View job


Code Review Complete

I've completed a comprehensive review of PR #368, focusing on the latest changes and overall implementation. Here's my detailed analysis:


🔄 Changes Since Last Review

Commit 8d2804f introduced an important change:

  • Temporarily disabled parameterized error messages in the error mapper
  • All error messages with dynamic parameters (like {{param_1}}, {{param_2}}) have been commented out
  • Corresponding tests have been marked with .skip()
  • Test expectations updated to expect fallback messages instead

Impact: This is a significant functional regression - users will now see generic "An error occurred. Please try again later." instead of helpful messages like "Stake must be between 1.00 and 2,000.00 USD".


🔴 Critical Issues

1. Parameterized Error Messages Are Completely Disabled

Files: packages/shared/src/utils/error-mapping/error-message-mapper.ts

Issue: All parameterized error messages have been commented out, including essential validation messages:

  • StakeNotInRange - "Stake must be between {{param_1}} and {{param_2}}"
  • MinStake, MaxStake - Min/max stake limits
  • StopLossTooHigh/Low, TakeProfitTooHigh/Low - Risk management limits
  • BarrierNotInRange - Barrier validation
  • And 20+ other critical user-facing messages

User Impact: Users will see unhelpful generic error messages instead of actionable feedback. For example:

  • ❌ "An error occurred. Please try again later."
  • ✅ "Stake must be between 1.00 and 2,000.00 USD"

Root Cause: The comment in the mapper says "Temporarily commented out - messages with dynamic parameters" but doesn't explain why. This appears to be a temporary workaround for an unresolved issue.

Recommendation:

  1. DO NOT MERGE until parameterized messages are working
  2. Investigate why parameter substitution was disabled
  3. If there's a blocker with localization, consider using backend messages temporarily
  4. Add a TODO/JIRA ticket reference explaining the temporary nature

2. Missing Context: Why Were Parameterized Messages Disabled?

Impact: High - affects user experience across all trading flows

The PR description doesn't mention this change, and there's no issue/ticket reference. This suggests:

  • Either this was an emergency fix for a production bug
  • Or there's a fundamental issue with how localize() handles parameters that needs addressing

Questions for team:

  • Is this a known issue with @deriv-com/translations?
  • Is there a timeline for re-enabling these messages?
  • Should we use backend error messages as a fallback instead of generic text?

3. Commented-Out UI Components

Files:

  • packages/trader/src/AppV2/Containers/ContractDetails/contract-details.tsx:105
  • packages/trader/src/Modules/SmartChart/Components/top-widgets.tsx:58

Issue: Production code has commented-out components:

{/* <PayoutInfo contract_info={contract_info} /> */}
{/* {InfoBox} */}

Recommendation:

  • Either remove the commented code or re-enable it
  • If work-in-progress, add TODO comments with ticket references
  • Update corresponding tests (currently also commented out)

🟡 High Priority Issues

4. No Runtime Validation for Unsupported Languages

File: packages/shared/src/utils/constants/default-options.ts:7-23

Issue: UNSUPPORTED_LANGUAGES array exists but isn't used to prevent language switching to unsupported languages.

Risk: Users could potentially switch to unsupported languages through URL manipulation (?lang=DE) or API calls.

Recommendation:

// In common-store.js:changeSelectedLanguage
changeSelectedLanguage = async key => {
    if (UNSUPPORTED_LANGUAGES.includes(key)) {
        console.warn(`Language ${key} is not supported`);
        return Promise.reject(new Error(`Language ${key} is not supported`));
    }
    // ... rest of implementation
}

5. Potential Memory Leak in Language Change Timer

File: packages/core/src/Stores/common-store.js:91-100

Issue: changing_language_timer_id is cleared on subsequent language changes but not on store disposal.

Code:

changeCurrentLanguage(new_language) {
    if (this.current_language !== new_language) {
        if (this.changing_language_timer_id) clearTimeout(this.changing_language_timer_id);
        this.current_language = new_language;
        this.is_language_changing = true;
        this.changing_language_timer_id = setTimeout(() => {
            this.is_language_changing = false;
        }, 2500);
    }
}

Risk: If the store is destroyed while is_language_changing is true, the timer continues running.

Recommendation: Add cleanup in a dispose/destructor method, or convert to MobX reaction with proper disposal.


6. Language Switcher UI: No Loading State Indication

File: packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx:19-23

Issue: Clicking the language button triggers async changeSelectedLanguage() but provides no visual feedback.

Code Review:

onClick={() => {
    if (!is_mobile_language_menu_open) {
        setMobileLanguageMenuOpen(true);  // Just opens menu
    }
}}

User Experience Issue:

  • Language switching involves: API call → localStorage update → moment.js init → URL update → WebSocket reconnection
  • All this happens with no loading indicator
  • is_language_changing exists in the store but isn't used here

Recommendation: Show spinner or disabled state while is_language_changing === true.


🟢 Medium Priority Issues

7. Dynamic Validation Messages: Good Pattern, But Performance Note

File: packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts:17-18

Assessment: ✅ Good solution for language switching

Code:

const getDynamicMessage = (messageKey: string) => () => localize(messageKey);

Why this works well:

  • Validation messages are now functions that are called at validation time
  • localize() is called fresh each time, using current language
  • Language change reaction properly clears validation errors and regenerates rules

Performance consideration:

  • Each validation now calls a function instead of using a cached string
  • For typical validation frequency (on user input), this is acceptable
  • The validator properly handles function messages (line 39: typeof rule.options.message === 'function')

No changes needed - this is working as designed.


8. Inconsistent Error Handling Strategy

Files: Various

Observation: The codebase is partially migrating from inline localize() calls to centralized mapErrorMessage():

Old pattern:

localize('Error message')

New pattern:

mapErrorMessage(error)

Issue: Mixed approach makes maintenance harder. Some errors go through mapper, others don't.

Recommendation:

  • Document the migration strategy in a comment or CONTRIBUTING.md
  • Ensure all backend errors go through mapErrorMessage()
  • Keep frontend validation messages using localize() directly (as they do now)

9. Test Coverage Gaps

Files:

  • packages/trader/src/AppV2/Containers/ContractDetails/__tests__/contract-details.spec.tsx:215-218
  • packages/trader/src/Modules/SmartChart/Components/__tests__/top-widgets.spec.tsx

Issue: Multiple tests are commented out or skipped:

// it('should render the PayoutInfo component', async () => { ... });
it.skip('should substitute single parameter in error message', () => { ... });

Impact on Coverage:

  • Parameterized error message tests: 6 tests skipped
  • UI component tests: 2 tests commented out

Recommendation:

  • Re-enable parameterized error tests once feature is restored
  • Either remove or re-enable UI component tests
  • If tests are temporarily disabled, add comments explaining why

✅ Things Done Well

10. Excellent Validation Error Clearing on Language Change

File: packages/trader/src/Stores/Modules/Trading/trade-store.ts:710-717

Code:

reaction(
    () => this.root_store.common.current_language,
    () => {
        // Clear existing validation errors to prevent stale messages
        this.validation_errors = {};
        
        // Regenerate all validation rules with new language
        this.setValidationRules(getValidationRules());
        this.changeDurationValidationRules();
    }
);

Why this is good:

  • Properly reacts to language changes
  • Clears stale error messages (prevents showing old language)
  • Regenerates rules with new localized messages
  • Clean separation of concerns

No changes needed


11. Proper Language Initialization

File: packages/core/src/Stores/common-store.js:62-66

Code:

allowed_languages = Object.keys(getAllowedLanguages(UNSUPPORTED_LANGUAGES));
current_language = getInitialLanguage();

Good practices:

  • Language list properly filtered by environment
  • Initial language detection from URL/localStorage/browser
  • Observable setup for MobX reactivity

Well implemented


12. Language Switcher Footer Integration

File: packages/core/src/App/Containers/Layout/trading-hub-footer.jsx:137-142

Code:

<ToggleLanguageSettings
    is_settings_visible={is_language_settings_modal_on}
    toggleSettings={toggleLanguageSettingsModal}
    lang={current_language}
    showPopover={showPopover}
/>

Assessment:

  • Clean component integration
  • Properly observes current_language from store
  • Modal state management is correct

Good implementation


🔒 Security Review

No Critical Security Issues Found

Reviewed:

  • ✅ XSS Prevention: All user input goes through localize() which handles escaping
  • ✅ No Secret Exposure: Language codes are safe to expose
  • ✅ Auth Flows: Language switching properly triggers re-authorization
  • ✅ Error Messages: Don't leak sensitive information (backend messages are mapped to generic frontend messages)
  • ✅ URL Parameters: Language param (?lang=) is properly sanitized through getAllowedLanguages()

Minor Recommendation: Add validation for UNSUPPORTED_LANGUAGES check (see issue #4 above).


⚡ Performance Review

Language Change Performance

Flow:

  1. User clicks language selector
  2. changeSelectedLanguage() called
  3. API call: WS.setSettings() (~100-500ms)
  4. Moment.js reinitialization (~50ms)
  5. Locale loading (~100ms)
  6. WebSocket reconnection (~200-1000ms)
  7. Component re-renders (React reconciliation)

Total: ~500ms - 2 seconds

Optimization notes:

  • ✅ 2.5-second debounce on is_language_changing prevents rapid re-renders
  • ✅ Validation rules regenerated once, not on every render
  • ⚠️ WebSocket reconnection might cause brief data fetch delay
  • ⚠️ All components using localize() will re-render (consider memoization for heavy components)

Recommendation: Add React.memo() to performance-critical components that use translated strings.


🧪 Test Coverage Assessment

Coverage Status

Well-tested:

  • ✅ Error message mapper: 148 lines of tests
  • ✅ Validation rule tests updated for function messages
  • ✅ Common store observable tests exist

Missing/Inadequate:

  • ⚠️ No E2E tests for language switching flow
  • ⚠️ No integration tests for validation message language updates
  • ⚠️ 6 error mapper tests skipped (parameterized messages)
  • ⚠️ UI component tests commented out

Test Quality:

// Good: Properly tests function message evaluation
const message = (multiplier_validation_rules.stop_loss.rules[0][1] as TExtendedRuleOptions).message;
expect(typeof message === 'function' ? message() : message).toBe('Please enter a stop loss amount.');

Recommendation:

  1. Add integration test: change language → verify validation messages update
  2. Add E2E test: full language switching flow
  3. Re-enable skipped tests once parameterized errors are fixed

📋 React/TypeScript Best Practices

Good Practices Observed

Observer Pattern:

const MenuTitle = observer(() => {
    const { common, ui } = useStore();
    // Component properly observes store changes
});

Type Safety:

export type TErrorFields = 'take_profit' | 'stop_loss' | 'date_start' | 'stake' | 'amount';

Functional Components: Consistent use of functional components with hooks

Could Be Improved

⚠️ Memoization:

// Current (menu-title.tsx:27)
{TranslationFlag[current_language] && TranslationFlag[current_language](22, 16)}

// Better - memoize the flag rendering
const flagIcon = useMemo(() => 
    TranslationFlag[current_language]?.(22, 16),
    [current_language]
);

⚠️ Error Boundary: No error boundary around language switching could cause app crash if localization fails


📊 Summary

Merge Blockers (Must Fix Before Merge)

  1. 🔴 CRITICAL: Parameterized error messages are completely disabled, causing poor UX
  2. 🔴 CRITICAL: No explanation for why parameterized errors were disabled
  3. 🔴 HIGH: Commented-out production code needs resolution

Recommended Fixes

  1. 🟡 HIGH: Add runtime validation for unsupported languages
  2. 🟡 HIGH: Fix timer cleanup memory leak
  3. 🟡 HIGH: Add loading state for language switching UI
  4. 🟢 MEDIUM: Document error handling migration strategy
  5. 🟢 MEDIUM: Re-enable or remove skipped tests

Positive Highlights

  • ✅ Dynamic validation message pattern is well-designed
  • ✅ Language change reaction properly clears stale errors
  • ✅ Clean integration of language switcher in UI
  • ✅ No security vulnerabilities found
  • ✅ Good test coverage for non-parameterized features

🎯 Recommendation

DO NOT MERGE until issue #1 (parameterized error messages) is resolved. This is a significant UX regression that will affect all users.

Once parameterized errors are working, this PR will restore language switching functionality effectively. The underlying architecture for dynamic validation messages is solid.


Review completed by Claude Code

@matin-deriv
Copy link
Collaborator

Some of the Claude comments worth looking into

…ge method

feat: Temporarily hide PayoutInfo component in ContractDetails
feat: Temporarily hide InfoBox in TopWidgets component
@claude
Copy link

claude bot commented Oct 23, 2025

Claude finished @akmal-deriv's task —— View job


Code Review Complete

I've completed a comprehensive review of PR #368, focusing on correctness, code quality, performance, security, and testing. Here's my detailed analysis:


🔴 High Priority Issues

1. Commented-Out UI Components Need Resolution

Files:

  • packages/trader/src/AppV2/Containers/ContractDetails/contract-details.tsx:106
  • packages/trader/src/Modules/SmartChart/Components/top-widgets.tsx:59

Issue: Production code contains commented-out components with "Hidden as we disable longcode temporarily" comments:

{/* <PayoutInfo contract_info={contract_info} /> */}
{/* {InfoBox} */}

Impact: These appear to be intentionally hidden features, but there's no tracking issue or explanation of when they'll be restored.

Recommendation:

  • Add TODO comments with ticket references (e.g., // TODO: Re-enable when longcode is supported - JIRA-123)
  • Or remove the code entirely if it won't be used
  • Update corresponding tests that are also commented out

2. All Parameterized Error Messages Are Disabled

File: packages/shared/src/utils/error-mapping/error-message-mapper.ts

Issue: Based on commit 8d2804f and the test file, all parameterized error messages have been commented out. This includes critical validation messages:

  • StakeNotInRange - Users won't see "Stake must be between 1.00 and 2,000.00"
  • MinStake, MaxStake - No specific limits shown
  • BarrierNotInRange - Validation details hidden
  • 20+ other messages with dynamic parameters

User Impact: Users see generic "An error occurred. Please try again later." instead of actionable feedback like specific stake limits or barrier ranges.

Root Cause: The comment says "Temporarily commented out - messages with dynamic parameters" but doesn't explain why. This appears related to the commit message "fix: Update error message mapping to use error code instead of subcode".

Recommendation:

  1. This is a significant UX regression - consider if this PR should merge in this state
  2. If parameterized messages are temporarily disabled due to a blocker, document it in the PR description
  3. Add a follow-up issue/ticket to track re-enabling these messages
  4. Consider falling back to backend messages (which include parameters) instead of generic text

3. Runtime Validation for Unsupported Languages ✅ IMPLEMENTED

File: packages/core/src/Stores/common-store.js:103-105

Status:GOOD - This was addressed in commit 447e068:

if (UNSUPPORTED_LANGUAGES.includes(key)) {
    return Promise.reject(new Error(`Language ${key} is not supported`));
}

This properly prevents switching to unsupported languages via URL manipulation or API calls.


🟡 Medium Priority Issues

4. Timer Memory Leak Risk

File: packages/core/src/Stores/common-store.js:91-100

Issue: changing_language_timer_id is cleared on subsequent language changes but not on store destruction:

changeCurrentLanguage(new_language) {
    if (this.changing_language_timer_id) clearTimeout(this.changing_language_timer_id);
    this.current_language = new_language;
    this.is_language_changing = true;
    this.changing_language_timer_id = setTimeout(() => {
        this.is_language_changing = false;
    }, 2500);
}

Risk: If the store is destroyed while is_language_changing is true, the timer continues running.

Recommendation: Add cleanup in a dispose/destructor method, or convert to MobX reaction with proper disposal. This is low-probability but should be addressed for robustness.


5. No Loading State Indication During Language Switch

File: packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx:19-23

Issue: Clicking the language button opens the menu but doesn't show any loading state during the async changeSelectedLanguage() operation:

onClick={() => {
    if (!is_mobile_language_menu_open) {
        setMobileLanguageMenuOpen(true);  // Just opens menu
    }
}}

User Experience: Language switching involves:

  • API call → localStorage update → moment.js init → URL update → WebSocket reconnection
  • All this happens with no loading indicator
  • is_language_changing exists in the store but isn't used in the UI

Recommendation: Show a spinner or disabled state while is_language_changing === true to provide user feedback during the async operation.


6. Test Coverage Gaps

Files:

  • packages/shared/src/utils/error-mapping/__tests__/error-message-mapper.spec.ts (6 tests skipped)
  • packages/trader/src/AppV2/Containers/ContractDetails/__tests__/contract-details.spec.tsx:215-218 (tests commented)
  • packages/trader/src/Modules/SmartChart/Components/__tests__/top-widgets.spec.tsx (tests commented)

Issue: Multiple tests are skipped or commented out:

it.skip('should substitute single parameter in error message', () => { ... });
// it('should render the PayoutInfo component', async () => { ... });

Recommendation:

  • Re-enable parameterized error tests once feature is restored
  • Either remove or re-enable UI component tests
  • Add integration test for language switching: change language → verify validation messages update
  • Consider E2E test for full language switching flow

✅ Things Done Well

7. Excellent Dynamic Validation Message Pattern

File: packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts:17-18

Implementation:

const getDynamicMessage = (messageKey: string) => () => localize(messageKey);

// Usage:
['req', { message: getDynamicMessage('Amount is a required field.') }]

Why this is excellent:

  • Validation messages are functions called at validation time, using current language
  • Validator properly handles function messages at line 39: typeof rule.options.message === 'function' ? rule.options.message() : rule.options.message
  • This enables validation messages to update when language changes
  • Clean, elegant solution to the language switching problem

No changes needed


8. Proper Language Change Reaction

File: packages/trader/src/Stores/Modules/Trading/trade-store.ts:710-722

Implementation:

reaction(
    () => this.root_store.common.current_language,
    () => {
        // Clear existing validation errors to prevent stale messages
        this.validation_errors = {};
        
        // Regenerate all validation rules with new language
        this.setValidationRules(getValidationRules());
        this.changeDurationValidationRules();
    }
);

Why this is excellent:

  • Properly reacts to language changes
  • Clears stale error messages (prevents showing old language)
  • Regenerates rules with new localized messages
  • Clean separation of concerns

Well implemented


9. Robust Error Message Mapper Architecture

File: packages/shared/src/utils/error-mapping/error-message-mapper.ts

Good practices:

  • Centralized error mapping with comprehensive switch statement
  • Graceful fallback to backend messages when subcodes aren't mapped
  • Well-documented with JSDoc explaining parameter handling
  • Comprehensive test coverage (148 lines of tests)
  • Proper null/undefined handling

Note: While parameterized messages are currently disabled (issue #2), the underlying architecture is solid and ready for when they're re-enabled.


10. Clean Language Switcher Integration

Files:

  • packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx
  • packages/core/src/App/Containers/Layout/trading-hub-footer.jsx

Implementation:

  • Clean component integration with MobX observers
  • Properly observes current_language from store
  • Modal state management is correct
  • Translation flags render conditionally

Good implementation


🔒 Security Review

Status:No security vulnerabilities found

Checked:

  • XSS Prevention: All user input goes through localize() which handles escaping
  • No Secret Exposure: Language codes are safe to expose
  • Auth Flows: Language switching properly triggers re-authorization (line 128: this.root_store.client.setIsAuthorize(false))
  • Error Messages: Don't leak sensitive information (backend messages are mapped to generic frontend messages)
  • URL Parameters: Language param (?lang=) is validated through UNSUPPORTED_LANGUAGES check
  • WebSocket Reconnection: Properly handled with new language parameter

⚡ Performance Analysis

Language Change Flow Performance:

  1. User clicks language selector
  2. changeSelectedLanguage() called
  3. API call: WS.setSettings() (~100-500ms)
  4. Moment.js reinitialization (~50ms)
  5. Locale loading (~100ms)
  6. WebSocket reconnection (~200-1000ms)
  7. Component re-renders (React reconciliation)

Total: ~500ms - 2 seconds

Optimization notes:

  • ✅ 2.5-second debounce on is_language_changing prevents rapid re-renders
  • ✅ Validation rules regenerated once, not on every render
  • ✅ Socket cache is cleared (line 106) to prevent stale data
  • ⚠️ WebSocket reconnection might cause brief data fetch delay
  • ⚠️ All components using localize() will re-render

Recommendations:

  1. Consider using React.memo() for performance-critical components that use translated strings
  2. The menu-title.tsx could benefit from useMemo for flag rendering:
const flagIcon = useMemo(() => 
    TranslationFlag[current_language]?.(22, 16),
    [current_language]
);

📋 React/TypeScript Best Practices

Good Practices Observed:

  • ✅ Consistent use of observer() HOC for MobX components
  • ✅ Proper TypeScript typing with interfaces (TTradeStore, ErrorObject, etc.)
  • ✅ Functional components with hooks
  • ✅ Clean separation of concerns (store logic vs UI)

Could Be Improved:

  • ⚠️ Memoization: Components that render translation flags could use useMemo (see performance section)
  • ⚠️ Error Boundary: No error boundary around language switching - if localization fails, could crash app

📊 Summary & Recommendations

Critical Items (Must Address):

  1. 🔴 Issue build(deps-dev): bump axios from 1.7.9 to 1.11.0 #2: Parameterized error messages disabled - significant UX regression

    • Decision needed: Is this acceptable for merge, or should it be fixed first?
    • Add PR description explaining the temporary disable and tracking issue
  2. 🔴 Issue build(deps-dev): bump webpack-dev-server from 5.2.0 to 5.2.1 #1: Commented-out components need resolution

    • Add TODO comments with ticket references or remove the code

Recommended Fixes:

  1. 🟡 Issue build(deps): bump @babel/runtime from 7.26.7 to 7.27.6 #4: Add timer cleanup to prevent memory leaks
  2. 🟡 Issue build(deps-dev): bump form-data from 4.0.1 to 4.0.4 #5: Add loading state indication during language switching
  3. 🟡 Issue build(deps-dev): bump brace-expansion from 1.1.11 to 1.1.12 #6: Re-enable or remove commented tests

Positive Highlights:

  • ✅ Dynamic validation message pattern is well-designed
  • ✅ Language change reaction properly clears stale errors
  • ✅ Clean integration of language switcher in UI
  • ✅ No security vulnerabilities found
  • ✅ Unsupported language validation implemented correctly
  • ✅ Good test coverage for non-parameterized features

🎯 Final Recommendation

Conditional Approval with Required Fixes:

The language switching functionality is well-implemented with a solid architecture. However, the parameterized error messages being disabled (Issue #2) is a significant UX regression that should be addressed.

Options:

  1. If merging as-is: Add clear documentation in PR description explaining:

    • Why parameterized errors are temporarily disabled
    • Tracking issue for re-enabling them
    • Timeline for restoration
  2. If holding merge: Fix parameterized error messages before merging

The other issues (#1, #4, #5, #6) should be addressed but are not merge-blockers.


Questions for the team:

  • Is there a specific blocker preventing parameterized error messages from working?
  • Is there a timeline for re-enabling the PayoutInfo and InfoBox components?
  • Should we add integration tests for language switching before merging?

@claude
Copy link

claude bot commented Oct 23, 2025

Claude finished @akmal-deriv's task —— View job


PR Review Complete ✓

I've completed a comprehensive review of PR #368 focusing on correctness, code quality, performance, security, and testing. Here are my findings:


🔴 High Priority Issues

1. Commented-Out Production Code Needs Resolution

Files:

  • packages/trader/src/AppV2/Containers/ContractDetails/contract-details.tsx:106
  • packages/trader/src/Modules/SmartChart/Components/top-widgets.tsx:59

Issue: Production components are commented out with "Hidden as we disable longcode temporarily":

{/* <PayoutInfo contract_info={contract_info} /> */}
{/* {InfoBox} */}

Recommendation:

  • Add TODO comments with ticket references (e.g., // TODO: Re-enable when longcode is supported - JIRA-XXX)
  • Or create a follow-up issue to track this work
  • Update corresponding test files that also have commented tests

2. All Parameterized Error Messages Are Disabled

File: packages/shared/src/utils/error-mapping/error-message-mapper.ts

Critical Issue: Based on commit 8d2804f, all parameterized error messages have been commented out. This includes ~20+ critical validation messages:

  • StakeNotInRange - Users won't see "Stake must be between X and Y"
  • MinStake, MaxStake - No specific limits shown
  • BarrierNotInRange - Validation details hidden
  • StopLossTooHigh/Low, TakeProfitTooHigh/Low - Risk management limits
  • And many more...

User Impact: Users will see generic "An error occurred. Please try again later." instead of actionable feedback with specific values.

Test Impact: 6 tests skipped in error-message-mapper.spec.ts

Recommendation:

  1. Document in PR description why this was disabled and the plan to re-enable
  2. Add a tracking issue/ticket reference
  3. Consider timeline impact on user experience
  4. Alternative: Use backend messages (which include parameters) instead of generic text

3. Language Validation ✅ IMPLEMENTED CORRECTLY

File: packages/core/src/Stores/common-store.js:103-105

Status:GOOD - Runtime validation properly implemented:

if (UNSUPPORTED_LANGUAGES.includes(key)) {
    return Promise.reject(new Error(`Language ${key} is not supported`));
}

This correctly prevents switching to unsupported languages.


🟡 Medium Priority Issues

4. Timer Memory Leak Risk

File: packages/core/src/Stores/common-store.js:91-100

Issue: changing_language_timer_id is cleared on subsequent language changes but not on store destruction:

changeCurrentLanguage(new_language) {
    if (this.changing_language_timer_id) clearTimeout(this.changing_language_timer_id);
    this.current_language = new_language;
    this.is_language_changing = true;
    this.changing_language_timer_id = setTimeout(() => {
        this.is_language_changing = false;
    }, 2500);
}

Risk: If the store is destroyed while is_language_changing is true, the timer continues running.

Recommendation: Add cleanup in a dispose/destructor method, or convert to MobX reaction with proper disposal.


5. No Loading State During Language Switch

File: packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx:19-23

Issue: Language button provides no visual feedback during async operation:

onClick={() => {
    if (!is_mobile_language_menu_open) {
        setMobileLanguageMenuOpen(true);  // Just opens menu
    }
}}

User Experience Issue: Language switching involves: API call → localStorage update → moment.js init → URL update → WebSocket reconnection (total: ~500ms-2s)

Store has is_language_changing but it's not used in the UI

Recommendation: Show spinner or disabled state while is_language_changing === true.


6. Test Coverage Gaps

Files:

  • packages/shared/src/utils/error-mapping/__tests__/error-message-mapper.spec.ts (6 tests skipped)
  • packages/trader/src/AppV2/Containers/ContractDetails/__tests__/contract-details.spec.tsx:215-218 (tests commented)
  • packages/trader/src/Modules/SmartChart/Components/__tests__/top-widgets.spec.tsx (tests commented)

Issue: Multiple tests are skipped or commented out due to disabled features.

Recommendation:

  • Re-enable parameterized error tests once feature is restored
  • Either remove or re-enable UI component tests
  • Add integration test: change language → verify validation messages update
  • Consider E2E test for full language switching flow

✅ Things Done Well

7. Excellent Dynamic Validation Message Pattern

File: packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts:17-18

Great implementation:

const getDynamicMessage = (messageKey: string) => () => localize(messageKey);

// Usage:
['req', { message: getDynamicMessage('Amount is a required field.') }]

Why this is excellent:

  • Validation messages are functions called at validation time, using current language
  • Validator properly handles function messages (line 39 in validator.ts)
  • Enables validation messages to update when language changes
  • Clean, elegant solution

No changes needed


8. Proper Language Change Reaction

File: packages/trader/src/Stores/Modules/Trading/trade-store.ts:710-722

Well-designed:

reaction(
    () => this.root_store.common.current_language,
    () => {
        // Clear existing validation errors to prevent stale messages
        this.validation_errors = {};
        
        // Regenerate all validation rules with new language
        this.setValidationRules(getValidationRules());
        this.changeDurationValidationRules();
    }
);

Why this is excellent:

  • Properly reacts to language changes
  • Clears stale error messages
  • Regenerates rules with new localized messages
  • Clean separation of concerns

No changes needed


9. Robust Error Message Mapper Architecture

File: packages/shared/src/utils/error-mapping/error-message-mapper.ts

Good practices:

  • Centralized error mapping with comprehensive switch statement
  • Graceful fallback to backend messages when subcodes aren't mapped
  • Well-documented with JSDoc
  • Comprehensive test coverage (148 lines of tests)
  • Proper null/undefined handling

Note: While parameterized messages are currently disabled (issue #2), the underlying architecture is solid and ready for when they're re-enabled.


10. Clean Language Switcher Integration

Files:

  • packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx
  • packages/core/src/App/Containers/Layout/trading-hub-footer.jsx

Good implementation:

  • Clean component integration with MobX observers
  • Properly observes current_language from store
  • Modal state management is correct
  • Translation flags render conditionally

🔒 Security Review

Status:No security vulnerabilities found

Checked:

  • XSS Prevention: All user input goes through localize() which handles escaping
  • No Secret Exposure: Language codes are safe to expose
  • Auth Flows: Language switching properly triggers re-authorization (line 128 in common-store.js)
  • Error Messages: Don't leak sensitive information (backend messages are mapped to generic frontend messages)
  • URL Parameters: Language param (?lang=) is validated through UNSUPPORTED_LANGUAGES check
  • WebSocket Reconnection: Properly handled with new language parameter

⚡ Performance Analysis

Language Change Flow:

  1. User clicks language selector
  2. changeSelectedLanguage() called
  3. API call: WS.setSettings() (~100-500ms)
  4. Moment.js reinitialization (~50ms)
  5. Locale loading (~100ms)
  6. WebSocket reconnection (~200-1000ms)
  7. Component re-renders

Total: ~500ms - 2 seconds

Optimization notes:

  • ✅ 2.5-second debounce on is_language_changing prevents rapid re-renders
  • ✅ Validation rules regenerated once, not on every render
  • ✅ Socket cache is cleared (line 106) to prevent stale data
  • ⚠️ WebSocket reconnection might cause brief data fetch delay
  • ⚠️ All components using localize() will re-render

Recommendations:

  1. Consider using React.memo() for performance-critical components that use translated strings
  2. The menu-title.tsx could benefit from useMemo for flag rendering:
const flagIcon = useMemo(() => 
    TranslationFlag[current_language]?.(22, 16),
    [current_language]
);

📋 React/TypeScript Best Practices

Good Practices Observed:

  • ✅ Consistent use of observer() HOC for MobX components
  • ✅ Proper TypeScript typing with interfaces (TTradeStore, ErrorObject, etc.)
  • ✅ Functional components with hooks
  • ✅ Clean separation of concerns (store logic vs UI)

Could Be Improved:

  • ⚠️ Memoization: Components that render translation flags could use useMemo
  • ⚠️ Error Boundary: No error boundary around language switching - if localization fails, could crash app

📊 Summary & Recommendations

Critical Items (Must Address):

  1. 🔴 Issue build(deps-dev): bump axios from 1.7.9 to 1.11.0 #2: Parameterized error messages disabled - significant UX regression

    • Decision needed: Is this acceptable for merge, or should it be fixed first?
    • Add PR description explaining the temporary disable and tracking issue
  2. 🔴 Issue build(deps-dev): bump webpack-dev-server from 5.2.0 to 5.2.1 #1: Commented-out production code needs resolution

    • Add TODO comments with ticket references or remove the code

Recommended Fixes:

  1. 🟡 Issue build(deps): bump @babel/runtime from 7.26.7 to 7.27.6 #4: Add timer cleanup to prevent memory leaks
  2. 🟡 Issue build(deps-dev): bump form-data from 4.0.1 to 4.0.4 #5: Add loading state indication during language switching
  3. 🟡 Issue build(deps-dev): bump brace-expansion from 1.1.11 to 1.1.12 #6: Re-enable or remove skipped tests

Positive Highlights:

  • ✅ Dynamic validation message pattern is well-designed
  • ✅ Language change reaction properly clears stale errors
  • ✅ Clean integration of language switcher in UI
  • ✅ No security vulnerabilities found
  • ✅ Unsupported language validation implemented correctly
  • ✅ Good test coverage for non-parameterized features

🎯 Final Recommendation

Conditional Approval with Required Discussion:

The language switching functionality is well-implemented with solid architecture. However, issue #2 (parameterized error messages being disabled) is a significant UX regression that needs discussion.

Options:

  1. If merging as-is: Add clear documentation in PR description:

    • Why parameterized errors are temporarily disabled
    • Tracking issue for re-enabling them
    • Timeline for restoration
  2. If holding merge: Fix parameterized error messages before merging

The other issues (#1, #4, #5, #6) should be addressed but are not merge-blockers.


Questions for the team:

  • Is there a specific blocker preventing parameterized error messages from working?
  • Is there a timeline for re-enabling the PayoutInfo and InfoBox components?
  • Should we add integration tests for language switching before merging?

View job

@akmal-deriv
Copy link
Collaborator Author

Some of the Claude comments worth looking into

Addressed important issues, but the critical issues it complains about are expected, the code was commented out for dynamic values and longcode

@claude
Copy link

claude bot commented Oct 23, 2025

Claude finished @akmal-deriv's task —— View job


Code Review Complete ✓

I've completed a comprehensive review of PR #368 focusing on correctness, code quality, performance, security, and testing. Based on the author's clarification that commented-out code and parameterized errors are intentional, I've adjusted my findings accordingly.


🎯 Overall Assessment

Status:Approve with Recommendations

This PR successfully restores language switcher functionality with a well-designed architecture for dynamic validation messages. The intentionally disabled features (parameterized errors, PayoutInfo, InfoBox) are properly documented with TODO comments as of commit 929c4e6.


🟢 Strengths & Excellent Implementations

1. Dynamic Validation Message Pattern

File: packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts:17-18

Excellent solution for language switching:

const getDynamicMessage = (messageKey: string) => () => localize(messageKey);

// Usage:
['req', { message: getDynamicMessage('Amount is a required field.') }]

Why this is excellent:

  • Messages are functions called at validation time, using current language
  • Validator properly handles this at line 39 in validator.ts: typeof rule.options.message === 'function' ? rule.options.message() : rule.options.message
  • Clean, elegant solution that enables real-time language updates

2. Proper Language Change Reaction

File: packages/trader/src/Stores/Modules/Trading/trade-store.ts:710-722

Well-designed language change handler:

reaction(
    () => this.root_store.common.current_language,
    () => {
        // Clear existing validation errors to prevent stale messages
        this.validation_errors = {};
        
        // Regenerate all validation rules with new language
        this.setValidationRules(getValidationRules());
        this.changeDurationValidationRules();
    }
);

Benefits:

  • Properly clears stale error messages
  • Regenerates rules with new localized messages
  • Clean separation of concerns

3. Language Validation Implemented Correctly

File: packages/core/src/Stores/common-store.js:103-105

Runtime validation properly prevents unsupported languages:

if (UNSUPPORTED_LANGUAGES.includes(key)) {
    return Promise.reject(new Error(`Language ${key} is not supported`));
}

This correctly blocks URL manipulation attempts (e.g., ?lang=DE).

4. Robust Error Message Mapper Architecture

File: packages/shared/src/utils/error-mapping/error-message-mapper.ts

  • Centralized error mapping with comprehensive switch statement
  • Graceful fallback to backend messages
  • Well-documented with JSDoc
  • Comprehensive test coverage (148 lines)
  • Proper null/undefined handling

5. Clean UI Integration

Files: menu-title.tsx, trading-hub-footer.jsx

  • Proper MobX observer pattern usage
  • Clean component integration
  • Correct modal state management

🟡 Medium Priority Recommendations

6. Timer Memory Leak Risk

File: packages/core/src/Stores/common-store.js:91-100

Issue: changing_language_timer_id is cleared on subsequent language changes but not on store destruction:

changeCurrentLanguage(new_language) {
    if (this.changing_language_timer_id) clearTimeout(this.changing_language_timer_id);
    this.current_language = new_language;
    this.is_language_changing = true;
    this.changing_language_timer_id = setTimeout(() => {
        this.is_language_changing = false;
    }, 2500);
}

Risk: If the store is destroyed while is_language_changing is true, the timer continues running.

Recommendation:

// Add a dispose method to CommonStore
dispose() {
    if (this.changing_language_timer_id) {
        clearTimeout(this.changing_language_timer_id);
    }
}

Or convert to MobX reaction with proper disposal handling.


7. No Loading State During Language Switch

File: packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx:19-23

Issue: Language button provides no visual feedback during async operation:

onClick={() => {
    if (!is_mobile_language_menu_open) {
        setMobileLanguageMenuOpen(true);  // Just opens menu
    }
}}

User Experience Impact: Language switching involves:

  • API call → localStorage update → moment.js init → URL update → WebSocket reconnection (total: ~500ms-2s)
  • Store has is_language_changing but it's not used in the UI

Recommendation:

<div className={is_language_changing ? 'loading' : ''}>
    {is_language_changing ? <Spinner /> : (
        TranslationFlag[current_language]?.(22, 16)
    )}
</div>

8. Performance: Memoization Opportunity

File: packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx:27

Current:

{TranslationFlag[current_language] && TranslationFlag[current_language](22, 16)}

Recommendation:

const flagIcon = useMemo(() => 
    TranslationFlag[current_language]?.(22, 16),
    [current_language]
);

Impact: Minor optimization, but good practice for components that may re-render frequently.


9. Test Coverage Gaps

Files:

  • packages/shared/src/utils/error-mapping/__tests__/error-message-mapper.spec.ts (6 tests skipped - intentional due to commented parameterized messages)
  • packages/trader/src/AppV2/Containers/ContractDetails/__tests__/contract-details.spec.tsx:215-218 (tests commented)
  • packages/trader/src/Modules/SmartChart/Components/__tests__/top-widgets.spec.tsx (tests commented)

Status: Tests properly commented/skipped due to intentional feature disabling ✓

Recommendation: Consider adding:

  • Integration test: change language → verify validation messages update
  • E2E test for full language switching flow (optional)

10. Error Boundary Consideration

Context: Language switching involves multiple async operations

Risk: If localization fails during language change, it could crash the app.

Recommendation: Consider wrapping language-sensitive components with error boundaries:

<ErrorBoundary fallback={<LanguageSwitchError />}>
    <MenuTitle />
</ErrorBoundary>

🔒 Security Review

Status:No security vulnerabilities found

Checked:

  • XSS Prevention: All user input goes through localize() which handles escaping
  • No Secret Exposure: Language codes are safe to expose
  • Auth Flows: Language switching properly triggers re-authorization (line 128: this.root_store.client.setIsAuthorize(false))
  • Error Messages: Don't leak sensitive information (backend messages are mapped to generic frontend messages)
  • URL Parameters: Language param (?lang=) is validated through UNSUPPORTED_LANGUAGES check
  • WebSocket Reconnection: Properly handled with new language parameter and socket cache clearing (line 106)

⚡ Performance Analysis

Language Change Flow Performance:

  1. User clicks language selector
  2. changeSelectedLanguage() called
  3. API call: WS.setSettings() (~100-500ms)
  4. Moment.js reinitialization (~50ms)
  5. Locale loading (~100ms)
  6. WebSocket reconnection (~200-1000ms)
  7. Component re-renders

Total: ~500ms - 2 seconds

Optimizations Observed:

  • ✅ 2.5-second debounce on is_language_changing prevents rapid re-renders
  • ✅ Validation rules regenerated once, not on every render
  • ✅ Socket cache is cleared (line 106) to prevent stale data
  • ⚠️ WebSocket reconnection might cause brief data fetch delay (expected)
  • ⚠️ All components using localize() will re-render (expected for language change)

Recommendations:

  1. Consider using React.memo() for performance-critical components with translations
  2. Current implementation is acceptable for typical language switch frequency

📋 React/TypeScript Best Practices

✅ Good Practices Observed:

  • Consistent use of observer() HOC for MobX components
  • Proper TypeScript typing with interfaces (TTradeStore, ErrorObject, etc.)
  • Functional components with hooks
  • Clean separation of concerns (store logic vs UI)

⚠️ Could Be Improved:

  • Add memoization for flag rendering (see recommendation Matin/Hooks Clean-up #8)
  • Consider error boundaries for language switching flow

📊 Code Quality Assessment

Correctness: ✅ Excellent

  • Language switching logic is sound
  • Proper validation and error handling
  • Edge cases handled (unsupported languages, null/undefined errors)

Code Quality: ✅ Good

  • Clean, readable code
  • Proper documentation with comments
  • Good use of TypeScript types

Performance: ✅ Good

  • No significant performance issues
  • Minor optimization opportunities noted

Security: ✅ Excellent

  • No vulnerabilities found
  • Proper input validation
  • Safe error handling

Testing: 🟡 Adequate

  • Good test coverage for enabled features
  • Tests properly skipped/commented for intentionally disabled features
  • Could benefit from integration/E2E tests

🎯 Summary

What Was Done Well:

  1. ✅ Dynamic validation message pattern is elegantly implemented
  2. ✅ Language change reaction properly clears stale errors
  3. ✅ Clean integration of language switcher in UI
  4. ✅ Unsupported language validation implemented correctly
  5. ✅ No security vulnerabilities
  6. ✅ Intentionally disabled features properly documented with TODO comments

Recommended Improvements (Non-Blocking):

  1. Add timer cleanup in store disposal to prevent memory leaks
  2. Show loading state during language switching for better UX
  3. Add memoization for flag rendering
  4. Consider error boundaries for language switching flow
  5. Add integration tests for language switching (when time permits)

Intentionally Disabled (As Expected):

  1. Parameterized error messages (commented out due to dynamic values)
  2. PayoutInfo component (commented out due to longcode being disabled)
  3. InfoBox component (commented out due to longcode being disabled)

All three are properly documented with TODO comments as of commit 929c4e6.


🎬 Final Recommendation

✅ APPROVE - This PR successfully restores language switcher functionality with a solid architecture. The intentionally disabled features are properly documented, and the core implementation is sound. The recommended improvements are minor and non-blocking.

The language switching implementation is well-designed, secure, and performant. Great work on the dynamic validation message pattern! 🎉


@nijil-deriv nijil-deriv merged commit abea3f0 into master Oct 23, 2025
11 checks passed
@nijil-deriv nijil-deriv deleted the f-enable-languages branch October 23, 2025 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants