@sixbell-telco/sdk
Version:
A collection of reusable components designed for use in Sixbell Telco Angular projects
293 lines (217 loc) • 7.58 kB
Markdown
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
---
```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);
}
```
---
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());
```
---
- The loader fetches all `translationPaths` for a language.
- JSON files are merged **deeply** from left to right.
- Last file wins on conflicts.
```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"]
```
---
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
```
---
```ts
bootstrapApplication(AppComponent, {
providers: [provideRuntimeTranslation('/assets/translation/translation.json')],
});
```
```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"
}
```
```ts
const translationService = inject(TranslationService);
translationService.enablePrefetch(false);
```
```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);
}
}
```
```ts
await translationService.waitForTranslations(10000);
```
```ts
bootstrapApplication(AppComponent, {
providers: [
provideRuntimeTranslation('http://localhost:4000/api/runtime/translation/config', {
sse: { url: 'http://localhost:4000/api/runtime/updates?resource=translation' },
}),
],
});
```
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`.
- Bump `TRANSLATION_SCHEMA_VERSION` and `meta.schemaVersion`.
---
- `@sixbell-telco/sdk/utils/runtime-config` - Loader + cache
- `@sixbell-telco/sdk/utils/theme` - Runtime theme system