feat: [DHIS2-21635] Display configurable terminology labels across UI - #4696
feat: [DHIS2-21635] Display configurable terminology labels across UI#4696henrikmv wants to merge 27 commits into
Conversation
…_plural-custom-terminology-note-relationship-attribute
…_plural-custom-terminology-note-relationship-attribute
…_display-custom-teminology
| 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](); | ||
| }; | ||
|
|
There was a problem hiding this comment.
🟡 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
| 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]); | ||
| }; |
There was a problem hiding this comment.
🔍 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
|




DHIS2-21635