Translation configurations
@flyze/lib-core-angular 1.0.0-alpha.34· latestA FyzTranslationConfig is a self-describing translation value: instead of a bare key or a bare
string, a config says how its text is produced. It exists because the flyze platform stores
translatable texts as data — a text may be a literal the user typed, a set of per-language literals,
or a reference into a transloco scope — and the consuming template must not care which.
FyzTranslateService resolves all three kinds. The pipe that renders them is described in
translate-pipes.
The three kinds
Section titled “The three kinds”FyzTranslationConfig is a discriminated union on _type
(fyz-translate-multi.model.ts:23):
_type | value | Resolved by |
|---|---|---|
static | string | interpolating the string as-is |
dynamic | FyzMultiLanguage[] — { language, text } entries | picking the entry whose language matches the active language |
scoped | FyzTranslocoConfig — { key, scope?, params? } | delegating to transloco through FyzTranslocoService |
All three may carry their own params. dynamic uses ISO 639-1 codes ('en', 'de') to match
transloco’s active language.
Resolution
Section titled “Resolution”FyzTranslateService offers a synchronous translate() and an observable selectTranslate(). Both
run the same three steps: normalize the input, merge the params, then switch on _type
(fyz-translate.service.ts:49).
The two differ in how they react to a language change:
static—selectTranslateemits once and completes. A static text has nothing to re-resolve.dynamic—selectTranslatemapsTranslocoService.langChanges$, so it re-emits the matching entry on every language switch.translate()readsgetActiveLang() || 'en'once. If no entry matches the active language the result is the empty string, not a fallback language.scoped— handed toFyzTranslocoService, which is backed by transloco’s ownselectTranslate/translate.
Malformed input never throws
Section titled “Malformed input never throws”#handleWrongInput
(fyz-translate.service.ts:136)
coerces anything that is not a config, because these values come from stored data and a bad one must
not blank a whole view:
| Input | Result |
|---|---|
null / undefined | the literal string NULL |
a string | treated as { _type: 'static' } |
an object with _type | used as-is |
| anything else | the literal string Unknown |
NULL and Unknown are deliberately visible in the UI — they are meant to be noticed and fixed at
the source, not silently swallowed.
Parameter precedence
Section titled “Parameter precedence”Params are merged least-specific first, so the call site always wins per key
(#mergeParams, :162):
value.params < config.params < opts.paramsvalue.params— only exists forscoped, where it sits inside theFyzTranslocoConfig. It is the oldest of the three and kept for stored configurations.config.params— defaults carried by the config itself.opts.params— what the template or caller passes.
Merging is per key, not all-or-nothing: a config carrying { name, role } overridden with
{ name } keeps its role. When none of the three has params, #mergeParams returns undefined,
which is what switches interpolation off entirely — see below.
Interpolation
Section titled “Interpolation”Interpolation goes through transloco’s own transpiler (TRANSLOCO_TRANSPILER,
fyz-translate.service.ts:25)
rather than a hand-written regex, so {{ … }} behaves exactly as it does inside translation files —
including custom delimiters configured through TRANSLOCO_CONFIG.
#interpolate
(fyz-translate.service.ts:179)
returns the value untouched when there are no params. This is not an optimization: the
transpiler replaces every placeholder it cannot resolve with an empty string, so transpiling
unconditionally would eat literal braces out of texts that were never meant to be interpolated. A
throwing custom transpiler is caught and the raw value returned — a broken transpiler must not break
the view.
Because FyzTranslateService injects TRANSLOCO_TRANSPILER, an app must configure transloco
(provideTransloco()) before this service is constructible.
Rationale for both choices: ADR 0002.
Scoped keys
Section titled “Scoped keys”FyzTranslocoService is the thin layer between a scoped config and transloco. Its job is turning
a key plus a scope into the flat key transloco actually holds
(fyz-transloco.service.ts:47):
buildScopedKey('HELLO', 'playground2'); // 'playground2.HELLO'buildScopedKey('HELLO', ['a', 'b']); // 'a.b.HELLO'reduceRight nests the scopes right to left, then duplicate dots are collapsed and a leading dot
stripped — so an empty scope segment does not produce ..HELLO.
Two behaviors worth knowing:
- Unresolved scoped keys fall back to the raw key.
selectTranslatecompares transloco’s result against the scoped key it asked for; transloco echoes the key back when it cannot resolve it, and that echo is mapped to the unscoped key (fyz-transloco.service.ts:34). So a missingplayground2.HELLOrenders asHELLO, notplayground2.HELLO. - The synchronous
translate()has no such fallback (fyz-transloco.service.ts:39). It returns whatever transloco returns, which for a missing key is the fully scoped key. The two methods therefore disagree on missing keys.
Both observable paths end in distinctUntilChanged(), so a subscriber only sees a value when the
rendered text actually changed.
Compatibility surface
Section titled “Compatibility surface”Every public method accepts a plain string where a config is expected, and the scoped params
live in two places. Neither is an accident — see
ADR 0006.
Related
Section titled “Related”- translate-pipes — rendering a config in a template
- runtime-scoped-translations — filling a scope at runtime
- guides/translating-text — how to use all of this