mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
* perf(web): keep lazy-only services and SafePipe out of main.js The eager shell imported barrels that re-export Angular injectables and a pipe it never uses, and their static definitions keep those modules in main.js: PlaylistFileImportService came with PlaylistContextFacade, normalizeDateLocale with SafePipe, and the workspace-shell-util barrel with SettingsContextService, which #1714 grew with match counts. That growth put master 108 bytes over the renderer.initialBytes baseline #1712 had measured on a branch without #1714. Add file-level entries for the three modules and use them from the eager and settings code: renderer.initialBytes 1,626,127 -> 1,619,993 bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(performance): lower the initial-bytes baseline to 1,619,993 bytes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
4.8 KiB
TypeScript
124 lines
4.8 KiB
TypeScript
import { registerLocaleData } from '@angular/common';
|
|
import localeEn from '@angular/common/locales/en';
|
|
import { inject, Injectable } from '@angular/core';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { normalizeDateLocale } from '@iptvnator/pipes/date-format';
|
|
import { createDevLogger } from '@iptvnator/shared/interfaces';
|
|
|
|
type LocaleDataModule = { default: unknown };
|
|
export type AppDateLocaleLoader = () => Promise<LocaleDataModule>;
|
|
|
|
const debug = createDevLogger('AppDateLocales');
|
|
|
|
/**
|
|
* Angular ships only English locale data; every other UI language needs its
|
|
* data registered before `DatePipe` can format with it. Each entry is a
|
|
* separate lazy chunk, keyed by the Angular locale id that
|
|
* `normalizeDateLocale()` produces for an app language (`by` -> `be`,
|
|
* `ary` -> `ar-MA`, `zhtw` -> `zh-Hant`). Importing all of them eagerly put
|
|
* the data for 18 languages into the initial bundle of every user.
|
|
*/
|
|
export const APP_DATE_LOCALE_LOADERS: Readonly<
|
|
Record<string, AppDateLocaleLoader>
|
|
> = {
|
|
ar: () => import('@angular/common/locales/ar'),
|
|
'ar-MA': () => import('@angular/common/locales/ar-MA'),
|
|
be: () => import('@angular/common/locales/be'),
|
|
de: () => import('@angular/common/locales/de'),
|
|
el: () => import('@angular/common/locales/el'),
|
|
es: () => import('@angular/common/locales/es'),
|
|
fr: () => import('@angular/common/locales/fr'),
|
|
hu: () => import('@angular/common/locales/hu'),
|
|
it: () => import('@angular/common/locales/it'),
|
|
ja: () => import('@angular/common/locales/ja'),
|
|
ko: () => import('@angular/common/locales/ko'),
|
|
nl: () => import('@angular/common/locales/nl'),
|
|
pl: () => import('@angular/common/locales/pl'),
|
|
pt: () => import('@angular/common/locales/pt'),
|
|
ru: () => import('@angular/common/locales/ru'),
|
|
tr: () => import('@angular/common/locales/tr'),
|
|
zh: () => import('@angular/common/locales/zh'),
|
|
'zh-Hant': () => import('@angular/common/locales/zh-Hant'),
|
|
};
|
|
|
|
const registeredLocales = new Set<string>(['en']);
|
|
const pendingLocales = new Map<string, Promise<void>>();
|
|
|
|
/**
|
|
* Registers the Angular locale data for an app language, loading it on first
|
|
* use. Resolves once `DatePipe` can format with that locale, so call it before
|
|
* `TranslateService.use()`: the language switch re-renders every date with the
|
|
* new locale, and a locale whose data has not arrived throws. English resolves
|
|
* immediately, as does a locale that is already registered or in flight.
|
|
*
|
|
* A failed load resolves rather than rejects, but never leaves the locale
|
|
* without data: English formatting is registered under the requested id so
|
|
* `DatePipe` keeps rendering instead of throwing in every template that passes
|
|
* the locale. The locale is not marked registered, so the next call retries
|
|
* the import and a success replaces the fallback.
|
|
*/
|
|
export function registerAppDateLocale(
|
|
language: string | null | undefined,
|
|
loaders: Readonly<
|
|
Record<string, AppDateLocaleLoader>
|
|
> = APP_DATE_LOCALE_LOADERS
|
|
): Promise<void> {
|
|
const locale = normalizeDateLocale(language);
|
|
if (registeredLocales.has(locale)) {
|
|
return Promise.resolve();
|
|
}
|
|
const inFlight = pendingLocales.get(locale);
|
|
if (inFlight) {
|
|
return inFlight;
|
|
}
|
|
const loader = loaders[locale];
|
|
if (!loader) {
|
|
debug('No bundled Angular locale data for', locale);
|
|
return Promise.resolve();
|
|
}
|
|
|
|
const load = loader()
|
|
.then((module) => {
|
|
registerLocaleData(module.default, locale);
|
|
registeredLocales.add(locale);
|
|
})
|
|
.catch((error: unknown) => {
|
|
debug(
|
|
'Loading Angular locale data failed; using English formatting for',
|
|
locale,
|
|
error
|
|
);
|
|
registerLocaleData(localeEn, locale);
|
|
})
|
|
.finally(() => {
|
|
pendingLocales.delete(locale);
|
|
});
|
|
pendingLocales.set(locale, load);
|
|
return load;
|
|
}
|
|
|
|
/** Gates UI language switches on the locale data they render with. */
|
|
@Injectable({ providedIn: 'root' })
|
|
export class AppDateLocaleService {
|
|
private readonly translate = inject(TranslateService);
|
|
private latestRequest = 0;
|
|
|
|
register(language: string | null | undefined): Promise<void> {
|
|
return registerAppDateLocale(language);
|
|
}
|
|
|
|
/**
|
|
* Registers the locale data, then switches the UI language. Switches are
|
|
* ordered by request, not by completion: a switch whose data arrives
|
|
* after a newer request was made is dropped, so the language chosen last
|
|
* is the one that ends up active.
|
|
*/
|
|
async use(language: string): Promise<void> {
|
|
const request = ++this.latestRequest;
|
|
await registerAppDateLocale(language);
|
|
if (request === this.latestRequest) {
|
|
this.translate.use(language);
|
|
}
|
|
}
|
|
}
|