mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat: introduce useLocalized hook for bilingual value retrieval.(localization round 2)
- Added useLocalized hook to provide a stable function for retrieving bilingual values based on the current language. - Updated various components across the portal and backoffice to utilize the new useLocalized hook for consistent bilingual label rendering. - Refactored localized function in licensing.helpers to handle empty Amharic strings correctly. - Enhanced localization handling in LicenseApplicationPage, ProfilePage, and other components to ensure proper language switching.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
export * from './licensing.types';
|
||||
export * from './licensing-api';
|
||||
export * from './licensing.helpers';
|
||||
export * from './use-localized';
|
||||
|
||||
@@ -155,7 +155,9 @@ export function applicantOrCompanyName(app: LicenseApplication): string | undefi
|
||||
/** Reads a bilingual value for the active language, falling back to English. */
|
||||
export function localized(value: Bilingual | undefined, language = 'en'): string {
|
||||
if (!value) return '';
|
||||
return (language === 'am' ? value.am : value.en) ?? value.en ?? value.am ?? '';
|
||||
// `||` not `??`: an empty Amharic string is "not translated", not a value —
|
||||
// editors save `am: ''` freely (ExamPage, CertificationPage, LocationForm).
|
||||
return (language === 'am' ? value.am : value.en) || value.en || value.am || '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,6 +248,9 @@ export function buildWizardSteps(
|
||||
* instead of showing an empty page.
|
||||
*/
|
||||
hasStaff?: boolean;
|
||||
/** Active UI language. Components get this from `useLocalized`; this is a
|
||||
* pure function, so the caller passes `i18n.language` through. */
|
||||
language?: string;
|
||||
},
|
||||
): WizardStep[] {
|
||||
const visible = [...sections]
|
||||
@@ -260,7 +265,7 @@ export function buildWizardSteps(
|
||||
if (!group) {
|
||||
steps.push({
|
||||
key: `section:${section.key}`,
|
||||
label: localized(section.title),
|
||||
label: localized(section.title, options?.language),
|
||||
kind: 'sections',
|
||||
sections: [section],
|
||||
});
|
||||
@@ -315,6 +320,7 @@ export type FieldErrors = Record<string, string>;
|
||||
export function validateSections(
|
||||
sections: FormSectionConfig[],
|
||||
formData: Record<string, Record<string, unknown>>,
|
||||
language = 'en',
|
||||
): FieldErrors {
|
||||
const errors: FieldErrors = {};
|
||||
|
||||
@@ -338,8 +344,8 @@ export function validateSections(
|
||||
if (field.required && empty) {
|
||||
errors[`${section.key}.${field.key}`] =
|
||||
field.type === 'BOOLEAN'
|
||||
? `${localized(field.label)} must be accepted`
|
||||
: `${localized(field.label)} is required`;
|
||||
? `${localized(field.label, language)} must be accepted`
|
||||
: `${localized(field.label, language)} is required`;
|
||||
continue;
|
||||
}
|
||||
if (empty) continue;
|
||||
|
||||
27
libs/api/src/lib/features/licensing/use-localized.ts
Normal file
27
libs/api/src/lib/features/licensing/use-localized.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { localized } from './licensing.helpers';
|
||||
import type { Bilingual } from './licensing.types';
|
||||
|
||||
/**
|
||||
* The component-facing bilingual reader — the twin of `useDateDisplayer()`.
|
||||
*
|
||||
* A hook rather than a bare `localized` import so the calling component is
|
||||
* subscribed to i18next: switching language re-renders it and every
|
||||
* backend-configured label flips with the rest of the UI. `useTranslation()`
|
||||
* with no instance argument resolves to the app's own <I18nextProvider>
|
||||
* (portal router.tsx, backoffice AppProviders.tsx), which is what makes this
|
||||
* work across two separate i18n instances.
|
||||
*
|
||||
* DISPLAY ONLY. Code that *matches* on a label — `.includes('nationality')`,
|
||||
* the vessel-picker regex in ConfigDrivenSection, the SUBCITY/WOREDA test in
|
||||
* AddressFormContent — must keep reading `value.en`, or the match breaks the
|
||||
* moment the user switches language.
|
||||
*
|
||||
* The returned function is stable per language, so it is safe — and required —
|
||||
* as a useMemo/useCallback dependency.
|
||||
*/
|
||||
export function useLocalized(): (value: Bilingual | undefined) => string {
|
||||
const { i18n } = useTranslation();
|
||||
return useCallback((value) => localized(value, i18n.language), [i18n.language]);
|
||||
}
|
||||
Reference in New Issue
Block a user