Skip to content

Fix #5652 - Runtime i18n Loading for Theme Translations (no build-time merge for i18n files)#5653

Open
kanasznagyzoltan wants to merge 6 commits into
DSpace:mainfrom
qultoltd:QREPO-405-Runtime-i18n-Loading-for-Theme-Translations
Open

Fix #5652 - Runtime i18n Loading for Theme Translations (no build-time merge for i18n files)#5653
kanasznagyzoltan wants to merge 6 commits into
DSpace:mainfrom
qultoltd:QREPO-405-Runtime-i18n-Loading-for-Theme-Translations

Conversation

@kanasznagyzoltan
Copy link
Copy Markdown
Contributor

@kanasznagyzoltan kanasznagyzoltan commented May 13, 2026

References

Description

Make the i18n TranslateLoader theme-aware: instead of relying on the build-time merge-i18n script, theme-specific translation overrides are loaded and merged at runtime from each configured theme's assets/<theme>/i18n/<lang>.json5, following the theme extends chain. This lets a single Docker image serve multiple customers/themes with their own translations.

Instructions for Reviewers

Changes in this PR:

  • src/ngx-translate-loaders/translate-server.loader.ts — After reading the base i18n file (dist/.../assets/i18n/<lang>.<hash>.json), the loader overlays every configured theme's dist/.../assets/<theme>/i18n/<lang>.json5 on top, in inheritance order (ancestor first, descendant last, so child theme keys win). The merged map is stored in NGX_TRANSLATE_STATE, so the browser does not re-fetch theme overrides after SSR.
  • src/ngx-translate-loaders/translate-browser.loader.ts — When the TransferState is empty (CSR / dev mode), the loader fetches the base i18n file plus each configured theme's override .json5 over HTTP and merges them client-side. Missing override files are tolerated via catchError.
  • src/ngx-translate-loaders/theme-i18n.util.ts (new) — Shared resolveThemeLoadOrder() helper used by both loaders. It walks each configured theme's extends chain up to the root ancestor and emits theme names in ancestor → descendant order, with deduplication and cycle protection. This follows the same theme extends inheritance DSpace already uses for component resolution (themed.component.ts).
  • Teststheme-i18n.util.spec.ts covers the load-order logic (extends chains, multi-level inheritance, deduplication, cycles); translate-browser.loader.spec.ts covers the browser merge (TransferState short-circuit, base + theme merge precedence, missing-file fallback). The server loader imports Node's fs/path, which cannot run in the browser-based Karma suite, so its ordering is covered by the shared-util spec and its merge mirrors the browser path.
  • No API or configuration changes. Themes without assets/<theme>/i18n/*.json5 files are ignored (missing file → empty object → no effect on the merged result). Existing setups that still run merge-i18n keep working unchanged.

How it works

Given a config like:

themes:
  - name: custom-child
    extends: custom-parent

…and i18n files at:

src/assets/i18n/hu.json5                          (base)
src/themes/custom-parent/assets/i18n/hu.json5     (parent overrides)
src/themes/custom-child/assets/i18n/hu.json5      (child overrides)

…the loader merges them at runtime in the order base → custom-parent → custom-child, so custom-child keys override custom-parent keys, which override base keys. If custom-child does not define a key, it falls back to custom-parent's value, then to base.

How to test

  1. Ensure src/assets/i18n/<lang>.json5 contains only base content (no merged theme content), and do not run npm run merge-i18n.
  2. Place theme-specific overrides in src/themes/<theme>/assets/i18n/<lang>.json5.
  3. Build (npm run build:prod) and serve the SSR output. On the home page, verify that theme-specific keys render the override value, that switching language keeps using the theme override, and that non-overridden keys fall back to the base translation.
  4. SSR hydration: in DevTools → Network, the initial HTML already contains the translated text, and no request to /assets/<theme>/i18n/<lang>.json5 fires on first navigation (served from NGX_TRANSLATE_STATE).
  5. CSR fallback: run npm run start:dev (no SSR) and confirm the loader fetches base + theme files over HTTP and merges them in the browser.
  6. Inheritance (optional): with a child theme that extends another, override a key only in the child and another only in the parent — both should resolve correctly when the child theme is active.
  7. No regression: a project with no theme i18n files behaves identically to before this PR.

Checklist

This checklist provides a reminder of what we are going to look for when reviewing your PR. You do not need to complete this checklist prior creating your PR (draft PRs are always welcome).
However, reviewers may request that you complete any actions in this list if you have not done so. If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!

  • My PR is created against the main branch of code (unless it is a backport or is fixing an issue specific to an older branch).
  • My PR is small in size (e.g. less than 1,000 lines of code, not including comments & specs/tests), or I have provided reasons as to why that's not possible.
  • My PR passes ESLint validation using npm run lint
  • My PR doesn't introduce circular dependencies (verified via npm run check-circ-deps)
  • My PR includes TypeDoc comments for all new (or modified) public methods and classes. It also includes TypeDoc for large or complex private methods.
  • My PR passes all specs/tests and includes new/updated specs or tests based on the Code Testing Guide.
  • My PR aligns with Accessibility guidelines if it makes changes to the user interface.
  • My PR uses i18n (internationalization) keys instead of hardcoded English text, to allow for translations.
  • My PR includes details on how to test it. I've provided clear instructions to reviewers on how to successfully test this fix or feature.
  • If my PR includes new libraries/dependencies (in package.json), I've made sure their licenses align with the DSpace BSD License based on the Licensing of Contributions documentation.
  • If my PR includes new features or configurations, I've provided basic technical documentation in the PR itself.
  • If my PR fixes an issue ticket, I've linked them together.

@lgeggleston lgeggleston added improvement i18n / l10n Internationalisation and localisation, related to message catalogs code task themes labels May 13, 2026
@lgeggleston lgeggleston moved this to 🙋 Needs Reviewers Assigned in DSpace 10.0 Release May 13, 2026
@lgeggleston
Copy link
Copy Markdown

Hi @kanasznagyzoltan, thank you for this PR! Since this would probably qualify as an improvement to build process rather than a new user feature, I added it to the 10.0 project to start. However since we're close to the May 22 code freeze, this will likely go towards a later release unless reviews are very quick for it.

@lgeggleston lgeggleston added the needs documentation PR is missing documentation. All new features and config changes require documentation. label May 13, 2026
@lgeggleston lgeggleston moved this to 🙋 Needs Reviewers Assigned in DSpace 11.0 Release Jun 2, 2026
@kanasznagyzoltan
Copy link
Copy Markdown
Contributor Author

Hi @kanasznagyzoltan, thank you for this PR! Since this would probably qualify as an improvement to build process rather than a new user feature, I added it to the 10.0 project to start. However since we're close to the May 22 code freeze, this will likely go towards a later release unless reviews are very quick for it.

Thank you @lgeggleston
I have updated my PR following the fresh release of the DSpace 10.0 release.
I’d appreciate it if you and others could take a look and share their's thoughts.

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

Labels

code task i18n / l10n Internationalisation and localisation, related to message catalogs improvement needs documentation PR is missing documentation. All new features and config changes require documentation. themes

Projects

Status: 🙋 Needs Reviewers Assigned

Development

Successfully merging this pull request may close these issues.

Runtime i18n Loading for Theme Translations (no build-time merge for i18n files)

2 participants