# 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')],
});
```

The runtime `translation.json` locale block keeps SDK formatting in sync with the active translation language. Use it together with `@sixbell-telco/sdk/utils/formatting` pipes and `FormattingService`.

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 JSON file that lists available languages, translation paths, and locale mappings. 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": {
    "updatedAt": "2026-02-02T00:00:00Z",
    "hash": "showcase-translations-v2",
    "schemaVersion": "3"
  },
  "additionalLanguages": [],
  "defaultLang": "es",
  "excludeLanguages": [],
  "translationPaths": ["/assets/i18n/", "/assets/i18n/sdk/", "/assets/themes/i18n"],
  "locale": {
    "defaultLocale": "en-US",
    "languageToLocale": {
      "en": "en-US",
      "es": "es-CL",
      "pt": "pt-BR"
    }
  }
}
```

**Rules**

- `translationPaths` should contain directories; each path loads `{lang}.json`.
- `defaultLang` must exist in available languages (base + additional - excluded).
- `locale.defaultLocale` is the final formatting fallback.
- `locale.languageToLocale` maps translation language codes to formatting locales.
- `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
- `currentLocale()` - locale resolved from the active language
- `ready()` - true when current language files are loaded
- `updateAvailable()` - set by optional update checks
- `lastRefreshFailed()` - true when the last refresh attempt failed

### Key Methods

```ts
translationService.setLanguage('es');
translationService.getAvailableLanguagesConfig();
translationService.getCurrentLocale();
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 = '3' as const;
export const TRANSLATION_SCHEMA_VERSION = '3' 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",
  "hash": "translations-1700000000000",
  "url": "/api/runtime/translation/config",
  "lang": "es"
}
```

### Showcase runtime mode wiring

The showcase uses all three runtime modes in `projects/showcase/src/app/app.config.ts`:

```ts
provideRuntimeTranslation('/assets/translation/translation.json');

provideRuntimeTranslation(`${environment.runtime.apiBaseUrl}/api/runtime/translation/config`, {
  updateStream: runtimeUpdateStream$,
});

provideRuntimeTranslation(`${environment.runtime.apiBaseUrl}/api/runtime/translation/config`, {
  sse: { url: `${environment.runtime.apiBaseUrl}/api/runtime/updates?resource=translation`, eventType: 'message' },
});
```

### 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
