UNPKG

@sixbell-telco/sdk

Version:

A collection of reusable components designed for use in Sixbell Telco Angular projects

293 lines (217 loc) 7.58 kB
# Translation Utility Runtime i18n system for Angular 19 with ngx-translate integration, runtime config loading, and resilient caching. This doc covers: - Translation config schema - Loading flow and cache behavior - Language switching and prefetch - Examples for initialization and usage - Runtime update handling --- ## Quick Start ```typescript import { provideRuntimeTranslation } from '@sixbell-telco/sdk/utils/translation'; bootstrapApplication(AppComponent, { providers: [provideRuntimeTranslation('/assets/translation/translation.json')], }); ``` In a component: ```typescript import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; import { TranslatePipe } from '@ngx-translate/core'; import { TranslationService } from '@sixbell-telco/sdk/utils/translation'; @Component({ selector: 'app-language-status', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: `{{ 'sdk.title' | translate }} ({{ translationService.currentLanguage() }})`, }) export class LanguageStatusComponent { protected readonly translationService = inject(TranslationService); } ``` --- ## Runtime Model Overview Translation config is a **single JSON** file that lists available languages and file paths. The loader: - Loads the config at boot. - Applies selected language and updates ngx-translate. - Prefetches other languages in background (default on). ### Mermaid: Boot Flow ```mermaid sequenceDiagram participant App participant Provider as provideRuntimeTranslation participant Loader as RuntimeConfigLoader participant Dexie as RuntimeConfigStore participant Service as TranslationService App->>Provider: bootstrap Provider->>Loader: loadLatest(config) Loader->>Dexie: get(cacheKey) alt cached & schema ok Dexie-->>Loader: cached Loader-->>Provider: config (source=cache) else cache miss / schema mismatch Loader->>Provider: fetch(config) Provider->>Loader: parse + store Loader->>Dexie: set(cacheKey) end Provider->>Service: applyRuntimeConfig() Service->>Service: setRuntimeConfig() + applyLanguage() Service->>Service: loadSelectedLanguage() Service-->>Service: prefetchRemainingLanguages() ``` --- ## Translation Config Schema ```json { "meta": { "version": "2026-02-02T00:00:00Z", "updatedAt": "2026-02-02T00:00:00Z", "hash": "showcase-translations-v1", "schemaVersion": "2" }, "additionalLanguages": [{ "code": "de", "name": "Deutsch" }], "defaultLang": "es", "excludeLanguages": ["pt"], "translationPaths": ["/assets/i18n/", "/assets/i18n/sdk/", "/assets/themes/i18n/"] } ``` **Rules** - `translationPaths` should contain directories; each path loads `{lang}.json`. - `defaultLang` must exist in available languages (base + additional - excluded). - `meta.schemaVersion` is required and invalidates cached configs when bumped. - `meta.hash` is required and drives update detection. - Runtime admin tooling should bump `meta.hash` when translation files change. --- ## Signals and Key APIs ### Signals - `currentLanguage()` - active language code - `ready()` - true when current language files are loaded - `updateAvailable()` - set by optional update checks ### Key Methods ```ts translationService.setLanguage('es'); translationService.getAvailableLanguages(); translationService.getDefaultLanguage(); translationService.withTranslationsReady(() => doWork()); ``` --- ## Loading and Merge Behavior - The loader fetches all `translationPaths` for a language. - JSON files are merged **deeply** from left to right. - Last file wins on conflicts. ### Mermaid: Merge Flow ```mermaid flowchart TD A[translationPaths] --> B[Fetch lang.json for each path] B --> C[Deep merge results] C --> D[ngx-translate cache] D --> E["ready() set true"] ``` --- ## Cache and Schema Versioning Translation config is cached in IndexedDB as last-known-good. When schema changes: - Bump `TRANSLATION_SCHEMA_VERSION`. - Update `meta.schemaVersion` in JSON. - Old cached config is discarded automatically. Hash requirements: - `meta.hash` is required and drives update detection. Default mode bootstrap validation: - Cached configs are validated on boot against the latest `meta.hash`. - If the hash changed, the current language reloads before render, and other languages refresh in the background. - If the network check fails, the cached config is used to keep the app available. When editing translation files manually, always update the runtime config `meta.hash` so clients detect the change. ```ts // projects/sdk/utils/translation/src/constants.ts export const TRANSLATION_SCHEMA_VERSION = '2' as const; ``` ```ts // note: TRANSLATION_SCHEMA_VERSION is exported from the translation public API // import from @sixbell-telco/sdk/utils/translation ``` --- ## Examples ### Basic Setup ```ts bootstrapApplication(AppComponent, { providers: [provideRuntimeTranslation('/assets/translation/translation.json')], }); ``` ### SSE Updates ```ts bootstrapApplication(AppComponent, { providers: [ provideRuntimeTranslation('/assets/translation/translation.json', { appId: 'host-app', sse: { url: 'http://localhost:4000/api/runtime/updates?resource=translation', eventType: 'message' }, }), ], }); ``` Translation update events must include `lang` so clients refresh only the changed locale: ```json { "resource": "translation", "version": "translation-update", "hash": "translations-1700000000000", "url": "/api/runtime/translation/config", "lang": "es" } ``` ### Disable Prefetch ```ts const translationService = inject(TranslationService); translationService.enablePrefetch(false); ``` ### Switch Language with UI ```ts import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; import { TranslationService } from '@sixbell-telco/sdk/utils/translation'; @Component({ selector: 'app-lang-switcher', changeDetection: ChangeDetectionStrategy.OnPush, template: ` <select (change)="setLanguage($event)"> @for (lang of translationService.getAvailableLanguagesConfig(); track lang.code) { <option [value]="lang.code">{{ lang.name }}</option> } </select> `, }) export class LangSwitcherComponent { protected readonly translationService = inject(TranslationService); setLanguage(event: Event): void { const lang = (event.target as HTMLSelectElement).value; this.translationService.setLanguage(lang); } } ``` ### Wait Until Ready ```ts await translationService.waitForTranslations(10000); ``` ### Using the local runtime API server ```ts bootstrapApplication(AppComponent, { providers: [ provideRuntimeTranslation('http://localhost:4000/api/runtime/translation/config', { sse: { url: 'http://localhost:4000/api/runtime/updates?resource=translation' }, }), ], }); ``` ## Hash and SchemaVersion Ownership In production, the backend or build pipeline that publishes runtime JSON should always set: - `meta.hash`: update this on any content change (language lists, paths, or values). - `meta.schemaVersion`: update only when the JSON structure changes. --- ## Troubleshooting ### Translations not updating - Verify `translationPaths` are correct and end with `/` or are normalized. - Verify files are named `{lang}.json`. ### Old config stuck in cache - Bump `TRANSLATION_SCHEMA_VERSION` and `meta.schemaVersion`. --- ## Related Utilities - `@sixbell-telco/sdk/utils/runtime-config` - Loader + cache - `@sixbell-telco/sdk/utils/theme` - Runtime theme system