Skip to content

feat: [DHIS2-21635] Display configurable terminology labels across UI - #4696

Draft
henrikmv wants to merge 27 commits into
masterfrom
hv/feat/DHIS2-21635_display-custom-teminology
Draft

feat: [DHIS2-21635] Display configurable terminology labels across UI#4696
henrikmv wants to merge 27 commits into
masterfrom
hv/feat/DHIS2-21635_display-custom-teminology

Conversation

@henrikmv

@henrikmv henrikmv commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

devin-ai-integration[bot]

This comment was marked as resolved.

@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 2 new potential issues.

Open in Devin Review

Comment on lines +21 to 31
const resolve = (
programId: string | undefined,
key: CustomLabelKey,
{ programId, stageId, plural }: StageOptions = {},
): string | undefined => {
const currentProgramId = useSelector(({ currentSelections }: any) => currentSelections.programId);
const currentStageId = useSelector(({ currentSelections }: any) => currentSelections.stageId);
const pId = programId ?? currentProgramId;
const sId = stageId ?? currentStageId;
return useMemo(() => {
const program = pId ? programCollection.get(pId) : undefined;
const stage = program && sId ? program.getStage(sId) : undefined;
return resolveLabel([stage?.customLabels, program?.customLabels], key, { plural });
}, [pId, sId, key, plural]);
{ stageId, plural }: TermLabelOptions,
): string => {
const program = programId ? programCollection.get(programId) : undefined;
const stage = program && stageId ? program.getStage(stageId) : undefined;
return resolveLabel([stage?.customLabels, program?.customLabels], key, { plural })
?? defaults[key]();
};

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 falls back to the generic English word instead of the administrator's custom wording

When a plural term is requested, the shared wording helper returns the built-in English word (defaults[key]() at src/core_modules/capture-core/metaData/helpers/customLabels/useLabel.ts:29) whenever no plural customisation exists, ignoring the configured singular wording, so users see default terminology in places that ask for plurals.
Impact: Screens that show plural terminology can display generic words like "enrollment" even though the administrator renamed the term (e.g. "Case"), and on servers that do not expose plural terminology this happens everywhere.

How the plural resolution degrades to the built-in term

resolveLabel (src/core_modules/capture-core/metaData/helpers/customLabels/customLabels.ts:39-47) returns pick(term.pluralField) when plural is requested and the key defines a plural field (enrollment, event, programStage). If the program/stage has only the singular custom label configured — or the plural fields were never requested because FEATURES.customTerminologyPlurals is unavailable on servers < 43 (src/core_modules/capture-core/metaDataStoreLoaders/programs/quickStoreOperations/storePrograms.ts:178-192) — pick yields undefined and resolve falls back to defaults[key](), which is the untranslated-to-custom generic term. A more sensible chain would be plural custom → singular custom → default.

Prompt for agents
In src/core_modules/capture-core/metaData/helpers/customLabels/useLabel.ts, the `resolve` helper asks `resolveLabel` for a plural label and, when nothing is found, immediately falls back to the hardcoded default term in `defaults`. For keys that define a pluralField (enrollment, event, programStage) `resolveLabel` returns undefined whenever the program/stage only configures the singular custom label, or whenever the plural fields were not fetched at all (server minor version < 43, see the customTerminologyPlurals gating in storePrograms.ts and WidgetEnrollment/hooks/useProgram.ts). The result is that a program renaming "enrollment" to "Case" will still show the generic default word for plural usages. Consider making the fallback chain plural custom label -> singular custom label -> translated default, so custom terminology is preserved even when no plural variant exists.
Open in Devin Review

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

Comment on lines +38 to 55
programId: string | undefined,
key: CustomLabelKey,
{ tetId, plural }: TrackedEntityTypeOptions = {},
): string | undefined => {
const currentTetId = useSelector(({ currentSelections }: any) => currentSelections.trackedEntityTypeId);
const id = tetId ?? currentTetId;
return useMemo(
() => resolveLabel(id ? trackedEntityTypesCollection.get(id)?.customLabels : undefined, key, { plural }),
[id, key, plural],
);
options: TermLabelOptions = {},
): string => resolve(programId, key, options);

/**
* React hook version — reads programId from Redux automatically.
* Pass programId explicitly to override (e.g. cross-program widgets).
*/
export const useTermLabel = (
key: CustomLabelKey,
options: TermLabelOptions & { programId?: string } = {},
): string => {
const { programId, stageId, plural } = options;
const currentProgramId = useSelector(({ currentSelections }: any) => currentSelections.programId);
const id = programId ?? currentProgramId;
return useMemo(() => resolve(id, key, { stageId, plural }), [id, key, stageId, plural]);
};

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.

🔍 New label API is exported but not consumed anywhere yet

getTermLabel/useTermLabel replace useProgramLabel/useStageLabel/useTrackedEntityTypeLabel, but a repo-wide search finds no call sites for either the removed hooks or the new helpers (only the re-exports in src/core_modules/capture-core/metaData/helpers/index.ts:20 and src/core_modules/capture-core/metaData/index.ts:44-47). The visible user-facing change in this PR is therefore only the static i18n string rewording ("Stage" -> "Program stage", etc.), while the configurable-terminology plumbing remains dormant. Worth confirming a follow-up PR wires it up, otherwise the fetched custom-label fields and the plural feature gate are dead weight.

Open in Devin Review

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

@henrikmv henrikmv changed the title feat: [DHIS2-21635] Display configurable terminology chore: [DHIS2-21969] Refine configurable terminology label support Aug 12, 2026
@henrikmv henrikmv changed the title chore: [DHIS2-21969] Refine configurable terminology label support feat: [DHIS2-21635] Display configurable terminology labels across UI Aug 13, 2026
@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

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