Skip to content

Commit 0da064f

Browse files
committed
refactor: Added API documentation and abstracted some internal methods
1 parent bbfe950 commit 0da064f

File tree

10 files changed

+275
-148
lines changed

10 files changed

+275
-148
lines changed

src/components/common/i18n/i18n-controller.ts

Lines changed: 146 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -12,135 +12,203 @@ import {
1212
dateRangePickerResourcesMap,
1313
} from './utils.js';
1414

15-
interface I18nControllerHost extends ReactiveControllerHost, Element {
16-
resourceStrings?: any;
15+
/**
16+
* Defines the structure for the host element that will use this controller.
17+
* The host must be a Lit element (ReactiveControllerHost) and an HTMLElement.
18+
*/
19+
interface I18nControllerHost extends ReactiveControllerHost, HTMLElement {
20+
// Properties the host is expected to have/use, though they are managed by the controller.
21+
resourceStrings?: unknown;
1722
locale?: string;
1823
}
1924

20-
type I18nControllerConfig = {
21-
defaultEN: any;
22-
onResourceChange?: (evt: CustomEvent<IResourceChangeEventArgs>) => void;
25+
type ResourceChangeCallback = (
26+
event: CustomEvent<IResourceChangeEventArgs>
27+
) => unknown;
28+
29+
/** Configuration object for the I18nController. */
30+
type I18nControllerConfig<T extends object> = {
31+
/** The full default English resource strings object for the component. */
32+
defaultEN: T;
33+
/** An optional callback to execute when the global locale changes. */
34+
onResourceChange?: ResourceChangeCallback;
2335
};
2436

25-
export class I18nController<T> implements ReactiveController {
26-
/** Set custom locale that overrides the global one. */
27-
public set locale(value: string) {
28-
this._locale = value;
29-
this._defaultResourceStrings = this.getCurrentResourceStrings();
37+
/**
38+
* Manages localization (i18n) for a Lit web component.
39+
* It handles the current locale, component-specific resource overrides,
40+
* and updates when the global localization state changes.
41+
*/
42+
class I18nController<T extends object> implements ReactiveController {
43+
//#region Internal properties and state
44+
45+
private readonly _host: I18nControllerHost;
46+
private readonly _defaultEN: T;
47+
48+
private _resourceChangeCallback?: ResourceChangeCallback;
49+
private _defaultResourceStrings: T;
50+
private _locale?: string;
51+
private _resourceStrings?: T;
52+
53+
//#endregion
54+
55+
//#region Public properties
56+
57+
/**
58+
* Sets a custom locale that overrides the global one for this host component instance.
59+
* Setting a new locale triggers an update of the resource strings.
60+
*/
61+
public set locale(value: string | undefined) {
62+
if (this._locale !== value) {
63+
this._locale = value;
64+
this._defaultResourceStrings = this._getCurrentResourceStrings();
65+
this._host.requestUpdate();
66+
}
3067
}
3168

32-
/** Get resolved locale for component */
33-
public get locale() {
69+
/**
70+
* Gets the resolved locale for the host component.
71+
* This is the component's custom locale if set, otherwise it falls back to the
72+
* global locale.
73+
*/
74+
public get locale(): string {
3475
return this._locale ?? getCurrentI18n();
3576
}
3677

3778
/**
3879
* Sets custom resource string for component with this controller.
3980
* Gets the resolved resource string for component.
4081
*/
41-
public set resourceStrings(value: T) {
42-
this._resourceStrings = value;
82+
public set resourceStrings(value: T | undefined) {
83+
if (this._resourceStrings !== value) {
84+
this._resourceStrings = value;
85+
this._host.requestUpdate();
86+
}
4387
}
4488

4589
/** Get resolved resource strings for component */
4690
public get resourceStrings(): T {
4791
return this._resourceStrings ?? this._defaultResourceStrings;
4892
}
4993

50-
private readonly _host: I18nControllerHost;
51-
private readonly _defaultEN: T;
52-
private readonly _resourceChangeHandler = this.onResourceChange.bind(this);
53-
private _locale: string | undefined;
54-
private _resourceStrings: T | undefined;
55-
private _defaultResourceStrings: T;
56-
private _resourceChangeCallback;
94+
//#endregion
5795

58-
constructor(host: I18nControllerHost, config: I18nControllerConfig) {
96+
//#region Life-cycle hooks and event listener
97+
98+
constructor(host: I18nControllerHost, config: I18nControllerConfig<T>) {
5999
this._host = host;
60100
this._defaultEN = config.defaultEN;
61-
this._defaultResourceStrings = this.getCurrentResourceStrings();
62-
this.registerResources(this._defaultEN, true);
101+
this._resourceChangeCallback = config.onResourceChange;
63102

64-
if (config?.onResourceChange) {
65-
this._resourceChangeCallback = config.onResourceChange;
66-
}
103+
this._defaultResourceStrings = this._getCurrentResourceStrings();
104+
this._registerResources(this._defaultEN);
67105

68106
this._host.addController(this);
69107
}
70108

71-
public registerResources(resource: T, isDefault = false) {
72-
const convertedResource = convertToCoreResource(resource);
73-
getI18nManager().registerI18n(
74-
convertedResource,
75-
isDefault ? getI18nManager().defaultLocale : this.locale
76-
);
77-
}
78-
79109
/** @internal */
80110
public hostConnected(): void {
81-
getI18nManager().addEventListener(
82-
'onResourceChange',
83-
this._resourceChangeHandler
84-
);
111+
getI18nManager().addEventListener('onResourceChange', this);
85112
}
86113

87114
/** @internal */
88115
public hostDisconnected(): void {
89-
getI18nManager().removeEventListener(
90-
'onResourceChange',
91-
this._resourceChangeHandler
92-
);
116+
getI18nManager().removeEventListener('onResourceChange', this);
93117
}
94118

95-
protected onResourceChange(event: CustomEvent<IResourceChangeEventArgs>) {
96-
this._defaultResourceStrings = this.getCurrentResourceStrings();
97-
if (this._resourceChangeCallback) {
98-
this._resourceChangeCallback(event);
99-
}
119+
/** @internal */
120+
public handleEvent(event: CustomEvent<IResourceChangeEventArgs>): void {
121+
this._defaultResourceStrings = this._getCurrentResourceStrings();
122+
this._resourceChangeCallback?.(event);
100123
this._host.requestUpdate();
101124
}
102125

103-
/** Get current resource strings based on default. Result is truncated result, containing only relevant locale strings. */
104-
protected getCurrentResourceStrings() {
105-
const normalizedResourceStrings: T = {} as T;
106-
const igcResourceStringKeys = Object.keys(this._defaultEN as any);
107-
const resourceStrings = getI18nManager().getCurrentResourceStrings(
126+
//#endregion
127+
128+
//#region Internal API
129+
130+
/** Registers the default English resources with the global i18n manager. */
131+
private _registerResources(resource: T): void {
132+
const convertedResource = convertToCoreResource(resource);
133+
const manager = getI18nManager();
134+
135+
manager.registerI18n(convertedResource, manager.defaultLocale);
136+
}
137+
138+
/**
139+
* Helper to find the correct resource map based on the component's default resources (`#defaultEN`).
140+
* This relies on structural checking (the component's key names).
141+
*/
142+
private _getResourceMapForComponent():
143+
| Map<string, string | undefined>
144+
| undefined {
145+
const keys = Object.keys(this._defaultEN);
146+
147+
if (keys.includes('last7Days')) {
148+
return dateRangePickerResourcesMap;
149+
}
150+
151+
if (keys.includes('selectMonth')) {
152+
return calendarResourcesMap;
153+
}
154+
155+
return undefined;
156+
}
157+
158+
/**
159+
* Gets the current, locale-specific resource strings for the component.
160+
* The logic maps component keys (from defaultEN) to core library keys
161+
* and retrieves the localized string from the i18n manager.
162+
*
163+
* Result is truncated, containing only relevant locale strings.
164+
*/
165+
private _getCurrentResourceStrings(): T {
166+
const coreResourceStrings = getI18nManager().getCurrentResourceStrings(
108167
this.locale
109168
);
110-
const resourceStringsKeys = Object.keys(resourceStrings);
111-
112-
for (const igcKey of igcResourceStringKeys) {
113-
const coreKey =
114-
calendarResourcesMap.get(igcKey) ??
115-
dateRangePickerResourcesMap.get(igcKey) ??
116-
undefined;
117-
if (coreKey && !coreKey.includes('i18n/')) {
118-
if (resourceStringsKeys.includes(coreKey)) {
119-
normalizedResourceStrings[igcKey as keyof T] = resourceStrings[
120-
coreKey as keyof IResourceStrings
121-
] as T[keyof T];
169+
const resourceMap = this._getResourceMapForComponent();
170+
171+
if (!resourceMap) {
172+
return coreResourceStrings as T;
173+
}
174+
175+
const normalizedResourceStrings: T = {} as T;
176+
const defaultComponentKeys = Object.keys(this._defaultEN) as (keyof T)[];
177+
178+
for (const igcKey of defaultComponentKeys) {
179+
const coreKey = resourceMap.get(igcKey as string);
180+
let resolvedValue: T[keyof T] = this._defaultEN[igcKey];
181+
182+
if (coreKey) {
183+
if (coreKey.includes('getWeekLabel')) {
184+
resolvedValue = getDisplayNamesFormatter().getWeekLabel(this.locale, {
185+
style: 'short',
186+
}) as T[keyof T];
122187
} else {
123-
normalizedResourceStrings[igcKey as keyof T] =
124-
this._defaultEN[igcKey as keyof T];
188+
resolvedValue =
189+
coreKey in coreResourceStrings
190+
? (coreResourceStrings[
191+
coreKey as keyof IResourceStrings
192+
] as T[keyof T])
193+
: this._defaultEN[igcKey];
125194
}
126-
} else if (coreKey?.includes('getWeekLabel')) {
127-
const weekLabel = getDisplayNamesFormatter().getWeekLabel(this.locale, {
128-
style: 'short',
129-
});
130-
normalizedResourceStrings[igcKey as keyof T] = weekLabel as T[keyof T];
131-
} else {
132-
// No mapped keys, no need to convert the resources then.
133-
return resourceStrings as T;
134195
}
196+
197+
normalizedResourceStrings[igcKey] = resolvedValue;
135198
}
136199

137200
return normalizedResourceStrings;
138201
}
202+
203+
//#endregion
139204
}
140205

141-
export function addI18nController<T>(
206+
/** Factory function to create and attach the I18nController to a host. */
207+
export function addI18nController<T extends object>(
142208
host: I18nControllerHost,
143-
config: I18nControllerConfig
144-
) {
209+
config: I18nControllerConfig<T>
210+
): I18nController<T> {
145211
return new I18nController<T>(host, config);
146212
}
213+
214+
export type { I18nController };

src/components/common/i18n/i18n.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from './EN/date-range-picker.resources.js';
2323
import { addI18nController, type I18nController } from './i18n-controller.js';
2424

25-
class TestLocalizedClass<T> extends LitElement {
25+
class TestLocalizedClass<T extends object> extends LitElement {
2626
public set locale(value: string) {
2727
this.i18nController.locale = value;
2828
}

0 commit comments

Comments
 (0)