Skip to content

chore: [DHIS2-21969] Refine configurable terminology label support - #4700

Draft
henrikmv wants to merge 27 commits into
masterfrom
hv/chore/DHIS2-21969_refine-configurable-terminology-support
Draft

chore: [DHIS2-21969] Refine configurable terminology label support#4700
henrikmv wants to merge 27 commits into
masterfrom
hv/chore/DHIS2-21969_refine-configurable-terminology-support

Conversation

@henrikmv

@henrikmv henrikmv commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

DHIS2-21969

This PR refactors the custom terminology system to support configurable labels from the DHIS2 backend. The key changes are:

  1. Unified label API: Replaces getProgramLabel, getStageLabel, useTrackedEntityTypeLabel, useProgramLabel, useStageLabel with two functions:

    • getTermLabel(programId, key, options) — usable anywhere
    • useTermLabel(key, options) — React hook that reads programId from Redux Both resolve custom labels from stage → program hierarchy, falling back to translated defaults.
  2. Feature-gated plural fields: Plural terminology fields (displayEnrollmentsLabel, displayProgramStagesLabel, displayEventsLabel) are only requested from the API when the backend supports them (version ≥ 43).

  3. i18n correctness: Module-level i18n.t() calls are moved to function/render scope so translations resolve at call time, not import time — required for dynamic terminology to work.

  4. String updates: "Stage" → "Program stage" across UI strings.

henrikmv added 27 commits July 23, 2026 08:24
…_plural-custom-terminology-note-relationship-attribute
…_plural-custom-terminology-note-relationship-attribute
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 New issue
1 New Code Smells (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment on lines +8 to 17
const defaults: Record<CustomLabelKey, () => string> = {
enrollment: () => i18n.t('enrollment'),
event: () => i18n.t('event'),
programStage: () => i18n.t('program stage'),
note: () => i18n.t('note'),
relationship: () => i18n.t('relationship'),
attribute: () => i18n.t('attribute'),
orgUnit: () => i18n.t('organisation unit'),
followUp: () => i18n.t('follow-up'),
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Plural wording requests fall back to singular words

When a plural term is requested but the server or configuration provides no plural custom label, the singular default word is returned (defaults[key]() at src/core_modules/capture-core/metaData/helpers/customLabels/useLabel.ts:29), so plural places in the interface can read "1 event"-style singular wording such as "event" instead of "events".
Impact: Users on servers that do not provide plural terminology (or programs without plural labels configured) can see grammatically wrong singular words where a plural is expected.

How the plural resolution path degrades to the singular default

resolveLabel in src/core_modules/capture-core/metaData/helpers/customLabels/customLabels.ts:43-55 now returns undefined when plural is requested and the plural field (displayEventsLabel, displayProgramStagesLabel, displayEnrollmentsLabel) is not present on the program/stage. Those plural fields are only fetched when the server supports customTerminologyPlurals (version ≥ 43) — see src/core_modules/capture-core/metaDataStoreLoaders/programs/quickStoreOperations/storePrograms.ts:178-196. So on pre-43 servers resolveLabel(..., { plural: true }) is always undefined, and resolve() falls back to defaults[key](), which only contains singular terms (i18n.t('event'), i18n.t('program stage'), i18n.t('enrollment'), …) with no plural variant. Previously resolveLabel fell back to the singular custom field for plural requests, which at least preserved the configured terminology. A plural default map (or i18n plural forms) is needed alongside the singular defaults.

Prompt for agents
In src/core_modules/capture-core/metaData/helpers/customLabels/useLabel.ts the `defaults` map only provides singular translations (e.g. i18n.t('event')). `resolve()` falls back to this map whenever `resolveLabel` returns undefined, which now happens for every plural request on servers below version 43 (plural fields such as displayEventsLabel are not requested there, see storePrograms.ts buildFieldsParam) and for any program that has not configured a plural label. As a result `getTermLabel(programId, 'event', { plural: true })` yields the singular word 'event'. Consider adding plural default translations (a second map or i18n.t with a count/plural form) and selecting them when options.plural is true, and/or deciding whether the removed fallback to the singular custom label should be reinstated for plural requests so configured terminology is not lost on older servers.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

header={
<div className={classes.header}>
<span>{i18n.t('Stages and Events')}</span>
<span>{i18n.t('Program stages and events')}</span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Renamed UI strings break existing Cypress assertions

"Stages and Events" is now rendered as "Program stages and events", but cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollmentNote/index.js:6 asserts cy.contains('Stages and Events'). Similarly, cypress/e2e/ScopeSelector/ScopeSelector.js:287 asserts cy.contains('Choose a stage for a new event'), which was renamed in src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.container.tsx:106. These e2e specs will fail until updated.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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