Skip to content

Relative translation keys

@flyze/lib-core-angular 1.0.0-alpha.34· latest

A library or a deeply nested feature module repeats its own prefix in every single key — FLYZE_LIB_CORE_ANGULAR.I18N.MAT_PAGINATOR.FIRST_PAGE_LABEL. TranslocoRelativeHelperService holds that prefix once and resolves keys relative to it, so call sites read 'FIRST_PAGE_LABEL'.

This is a separate mechanism from the scope in a scoped FyzTranslationConfig (translation-config): a scope is a transloco namespace loaded from its own file, a base path is a prefix inside an already loaded namespace.

TranslocoRelativeHelperService (transloco-relative-helper.service.ts:31) is not providedIn: 'root' — it is deliberately provided per module or component, because the base path is per consumer. Injecting the root instance from two features would have them fight over setBasePath.

The base path is a BehaviorSubject, and the select* methods switchMap over it (transloco-relative-helper.service.ts:57). That is the reason for the extra indirection: a subscriber that subscribed before setBasePath() was called still re-resolves against the new prefix, and a later setBasePath() re-resolves every open subscription. The synchronous translate() reads _basePath$.value directly, so it only sees the prefix already set.

MethodPrefixedReturns
translate(key, params?, lang?)yesstring
translateNoBase(key)nostring
selectTranslate(key, …)yesObservable
selectTranslateObject(key, …)yesObservable<object>
selectLanguageChanged(propPath?)noObservable<object>

The prefix is a plain string concatenation (`${basePath}${key}`) — no separator is inserted. A base path must therefore end with the dot itself, or every key must start with one. Passing '' resolves the base path as an object, which is what the paginator below relies on.

selectLanguageChanged is the odd one out: it ignores the base path, filters for object results and debounceTime(500), and still defaults its prop path to 'BRICK_GENERATOR_LIB' (transloco-relative-helper.service.ts:78) — a leftover from another library. Always pass an explicit path.

For code that has no injector, createTranslateRelative(basePath) returns a function closing over the prefix and calling transloco’s standalone translate() (transloco-relative-helper.service.ts:23). It has the same string-concatenation caveat, is synchronous only, and requires transloco to be initialized — useful in resolvers and factory providers, not in templates.

MatPaginatorIntlTranslocoService is the one consumer shipped with the library and the working example of the pattern (mat-paginator-intl-translation.service.ts:9). It extends Material’s MatPaginatorIntl, sets the base path to FLYZE_LIB_CORE_ANGULAR.I18N.MAT_PAGINATOR, then subscribes to selectTranslateObject('') — one subscription for the whole label block instead of six.

getRangeLabel is reassigned inside that subscription rather than overridden as a method, because it has to close over the freshly translated RANGE_SEPARATOR. The implementation is Material’s own, with the separator substituted for the hardcoded ' of ', including Material’s edge cases: 0 items renders 0<sep>0, and a start index past the end does not clamp the end index.

MatPaginatorIntl expects changes to be emitted when labels change; this service does not emit it. An already-rendered paginator therefore keeps its old labels until something else re-renders it — the labels are correct on first paint and after a language switch that triggers change detection anyway.

The keys themselves ship in src/lib/i18n/en.json and de.json; how they reach a consumer app is guides/loading-library-translations.