Skip to content

feat(i18n): add internationalization plugin with locale auto-detection and built-in translations#10800

Open
mmorel-35 wants to merge 4 commits intoswagger-api:masterfrom
mmorel-35:i18n
Open

feat(i18n): add internationalization plugin with locale auto-detection and built-in translations#10800
mmorel-35 wants to merge 4 commits intoswagger-api:masterfrom
mmorel-35:i18n

Conversation

@mmorel-35
Copy link
Copy Markdown

@mmorel-35 mmorel-35 commented Apr 7, 2026

Adds a lightweight i18n system to Swagger UI via a new Redux plugin. All user-facing strings become translatable at runtime with zero breaking changes — existing behavior is identical when no locale is configured.

Description

  • src/core/plugins/i18n/ — new I18nPlugin registered early in the base preset

    • actions.js / reducers.js / selectors.js — standard Redux slice for { locale, messages } state
    • fn.js — pure translate(localeMsgs, fallbackMsgs, key, vars?) + fallbackT for component-level English fallback without the system
    • locales/en.js — canonical English catalog (~98 keys, dot-namespaced: "button.execute", "aria.collapse_operation", etc.)
    • locales/index.js — re-exports all 9 built-in locale catalogs as a single keyed object
    • index.jsafterLoad loads English, auto-detects or applies configured locale, auto-loads the matching built-in locale, injects t(key, vars?) into rootInjects
  • src/core/config/defaults.js — single new option: locale: null (BCP 47 tag; null = read navigator.languages[0], normalize to base code, fall back to "en")

  • 40 components updated — all user-visible strings replaced with t("key") calls; every component that accepts t as a prop now declares static defaultProps = { t: fallbackT } (class components) or uses t = fallbackT as a default parameter (functional components), so all components degrade gracefully to English in isolated unit tests or standalone renders without the Redux system

  • src/core/components/param-body.jsx — corrected static defaultProp typo to static defaultProps (the React-recognized name) and updated the internal reference from ParamBody.defaultProp.consumes to ParamBody.defaultProps.consumes, ensuring default props are correctly applied when props are omitted

  • docs/usage/i18n.md — stale partial response.* key table replaced with a canonical reference to locales/en.js; docs accurately describe the lightweight key/value lookup without implying unsupported Intl formatting/pluralization APIs

The plugin auto-loads the matching built-in locale on startup — no loadMessages call is needed for supported languages:

// French translations load automatically — no loadMessages() call required
SwaggerUIBundle({ locale: "fr", url: "..." })

i18n / JavaScript quality improvements

  • Prototype-safe lookups — all key in obj / k in vars guards in fn.js and index.js replaced with Object.prototype.hasOwnProperty.call() to prevent inherited Object.prototype properties (toString, constructor, etc.) from matching translation keys
  • Consistent return typetranslate() always returns String(raw), eliminating a path that returned a raw non-string value
  • reducers.jsfromJS() (deep recursive conversion) replaced with Map() (correct shallow conversion for flat { key: string } catalogs)
  • Locale normalization — both auto-detected and configured locales are normalized to base language code (e.g. "fr-CA""fr") for consistent matching
  • TopBar propTypes — consolidated into a single static propTypes block to prevent the external assignment from silently overwriting layoutActions/authActions validation
  • response.jsx<code>Accept</code> inline element preserved in JSX using prefix/suffix i18n keys (response.controls_accept_header_prefix / _suffix) so the code styling is not lost in translation

Motivation and Context

Hard-coded English strings throughout Swagger UI make it impossible for non-English users to get a localized experience. This change introduces a zero-dependency, zero-breaking-change i18n layer that supports runtime locale switching and ships with 9 built-in translations.

How Has This Been Tested?

  • Unit tests: npm run test:unit — all tests pass
  • New test/unit/core/plugins/i18n/ test suite covering translate(), fallbackT(), prototype edge-cases, and locale key-set completeness for all 9 built-in catalogs
  • Manual testing with locale: "fr", "de", "zh" confirming auto-load and correct string rendering

Screenshots (if appropriate):

Checklist

My PR contains...

  • No code changes (src/ is unmodified: changes to documentation, CI, metadata, etc.)
  • Dependency changes (any modification to dependencies in package.json)
  • Bug fixes (non-breaking change which fixes an issue)
  • Improvements (misc. changes to existing features)
  • Features (non-breaking change which adds functionality)

My changes...

  • are breaking changes to a public API (config options, System API, major UI change, etc).
  • are breaking changes to a private API (Redux, component props, utility functions, etc.).
  • are breaking changes to a developer API (npm script behavior changes, new dev system dependencies, etc).
  • are not breaking changes.

Documentation

  • My changes do not require a change to the project documentation.
  • My changes require a change to the project documentation.
  • If yes to above: I have updated the documentation accordingly.

Automated tests

  • My changes can not or do not need to be tested.
  • My changes can and should be tested by unit and/or integration tests.
  • If yes to above: I have added tests to cover my changes.
  • If yes to above: I have taken care to cover edge cases in my tests.
  • All new and existing tests passed.

Related to #2488

…n and built-in translations

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
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.

1 participant