Skip to content

Feat/frontend locale config#1727

Open
AndreRFerreira wants to merge 70 commits into
mainfrom
feat/frontend-locale-config
Open

Feat/frontend locale config#1727
AndreRFerreira wants to merge 70 commits into
mainfrom
feat/frontend-locale-config

Conversation

@AndreRFerreira

Copy link
Copy Markdown
Collaborator

Summary:
Add configuration-driven locale resolution to support server-published locale policies, locale fallback chains, and restricted locale lists for multi-language deployments. Closes #1606

Description for the changelog:
Configuration-driven locale resolution: client reads locale policy from server configuration, resolves user locale against allowed locales with fallback chain, and surfaces configuration load errors in the UI

Declaration:
Thanks for submitting a pull request, please make sure:

[x] content meets the license for this project
[x] appropriate unit tests have been created and/or modified
[x] you have considered any changes required for the functional tests
[x] you have read the contribution guide and agree to the Code of Conduct
[] either no AI-generated content has been used in this pull request
[x ] or any use of AI in this pull request has been disclosed below:
AI Tools: DeepSeek
LLMs and versions: DeepSeek
Prompts: [Many]

Other info:

  • Locale resolver (src/service/locale/locale-resolver.js): Pure-function service that resolves user locale against a server-defined allowed-locale list with i18next-style fallback chain. Handles browser navigator.languages, server defaultLocale, and SUPPORTED_LOCALES intersection.
  • HTTP client timeout (src/service/api/api.js): Adds configurable default request timeout to prevent hung requests blocking locale resolution (spinners stay forever).
  • Config store module (src/store/modules/config.js): Sanitizes server config (validates booleans/strings against SUPPORTED_LOCALES), exposes allowedLocales/defaultLocale getters, and surfaces configError state for error handling.
  • Locale store module (src/store/modules/locale.js): Wires the resolver into Vuex — resolves locale on RESOLVE_LOCALE dispatch, selects locale on LOCALE_SELECTED, syncs i18n instance, and enforces the allowed-locale guard.
  • HomePage UI (src/views/HomePage.vue): Adds loading spinner while config is fetched, shows toast on config load failure, and extracts resolveProviders() as a pure function for testability.
  • LocaleSelect component: Wires locale resolution into dropdown selection.
  • i18n constants (src/i18n/index.js): Extracts DEFAULT_LOCALE, SUPPORTED_LOCALES, and installLegacyCompat() for reuse across modules.
  • Config load error translation (src/i18n/*.js): Adds home.errors.configLoadFailed key to all locale files.
  • E2E tests: Config API mock server, HomePage config-driven tests, locale section tests.
  • Unit tests: Full coverage for locale resolver, config store, locale store, config actions, locale actions, and i18n constants.

@AndreRFerreira AndreRFerreira self-assigned this Jun 10, 2026
@AndreRFerreira AndreRFerreira added I18N Enterprise Readiness Strategic pillar from Threat Dragon's roadmap enhancement New feature or request Usability UX & Accessibility Strategic pillar from Threat Dragon's roadmap labels Jun 10, 2026
@AndreRFerreira

AndreRFerreira commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Unrelated test fix coming up next: resetDesktopApp state pollution in desktop E2E menu tests

Problem: The 'opens a new model editor through File > New Model' test was failing intermittently with URL app://./index.html#/desktop/Saved%20As%20From%20Menu/edit instead of the expected app://./index.html#/desktop/New%20Threat%20Model/edit. The URL contained the title from a previous test (SAVED_AS_MODEL_TITLE), indicating cross-test state leakage.

Root cause: resetDesktopApp() cleared sessionStorage before browser.refresh(). Between the clear and the refresh, window.location.hash = '#/' triggered Vue Router navigation, causing Vuex mutations to fire. The vuex-persist plugin then re-wrote the stale threat model state back to sessionStorage. When the page reloaded, Vuex re-hydrated from the re-persisted stale data.

The Fix: Clear session/localStorage after the first page refresh, then do a second refresh so the app reinitializes from truly empty storage. (td.vue/tests/e2e/desktop/support/helpers.js)

Not related to my feature work — this is a pre-existing test isolation bug in the desktop E2E test infrastructure.

@AndreRFerreira

AndreRFerreira commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the support @lreading I've never used this before, so I’ve to lean on you here as I'll have to read about it rather than just using.
On the spinner (and both questions), sounds about right, I’ll refactor today, probably add/use a "isElectron" flag, then the issue will go away. I missed that.
Please do let me know on the review of other pieces where I’ve missed anything or for potential improvements.
EDIT: I see a conflict with .gitignore i'll address that later today also
EDIT: I see the issue with the e2e, adding a fixes for this also, hope it's the ones you'r are aware of

fix: serialize visual regression tests after e2e_tests to avoid port 8080 conflict

fix: visual tests reuse running site from e2e_tests, only start td.server

fix: visual tests need npm install (separate runner from e2e_tests)
…isDesktopApp)

Migrate all source files from the 'is-electron' npm package to the
internal @/service/environment module, which provides isDesktopApp().
This centralises the desktop detection logic and improves testability.
@AndreRFerreira

AndreRFerreira commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

I've taken some liberties in applying other bits and pieces, sorry for the larger review requirement

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Enterprise Readiness Strategic pillar from Threat Dragon's roadmap I18N Usability UX & Accessibility Strategic pillar from Threat Dragon's roadmap

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Personalized welcome experience - language selection

3 participants