{"_id":"@apappas1129/ngx-fluent","_rev":"2-c194ef003e463fc1b189cb312b3ac3f6","name":"@apappas1129/ngx-fluent","dist-tags":{"latest":"22.0.3"},"versions":{"22.0.0":{"name":"@apappas1129/ngx-fluent","version":"22.0.0","keywords":["i18n","internationalization","l10n","localization","ftl","locale","language","translate","translation"],"author":{"name":"Alex Pappas","email":"apappas1129@gmail.com"},"license":"MIT","_id":"@apappas1129/ngx-fluent@22.0.0","maintainers":[{"name":"apappas1129","email":"apappas1129@gmail.com"}],"contributors":[{"url":"https://www.zeferinix.com","name":"Cerino Ligutom III","email":"zeferinix@gmail.com"}],"homepage":"https://github.com/apappas1129/ngx-fluent#readme","bugs":{"url":"https://github.com/apappas1129/ngx-fluent/issues"},"dist":{"shasum":"fe34932769d21a60ef2367cfd9217614e4c84eda","tarball":"https://registry.npmjs.org/@apappas1129/ngx-fluent/-/ngx-fluent-22.0.0.tgz","fileCount":5,"integrity":"sha512-2vCia6/1306TzF7P7d9r57Xt8swfLmu14adtvWDL4dyEbOX/46pUz/b1w3MYa5Lm2CxTrcJW+if2hHjG+GKNFQ==","signatures":[{"sig":"MEQCIEc+WPBxQCo37miN9z5N3P942mEEN7TEtUPYMoJlFrjyAiAGBqHTqyZPIRIqORRQROx6w9IMwo1AbgzLbRcACcquDQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39107},"type":"module","module":"fesm2022/apappas1129-ngx-fluent.mjs","exports":{".":{"types":"./types/apappas1129-ngx-fluent.d.ts","default":"./fesm2022/apappas1129-ngx-fluent.mjs"},"./package.json":{"default":"./package.json"}},"gitHead":"f775f0c0ada5a8747fcc4611a54974d2057b7e17","typings":"types/apappas1129-ngx-fluent.d.ts","_npmUser":{"name":"apappas1129","email":"apappas1129@gmail.com"},"repository":{"url":"git+https://github.com/apappas1129/ngx-fluent.git","type":"git"},"_npmVersion":"11.13.0","description":"An Angular library for Fluent","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","dependencies":{"tslib":"^2.6.2"},"_hasShrinkwrap":false,"peerDependencies":{"@angular/core":"^22.0.0","@fluent/bundle":">= 0.18.0 < 1.0.0","@angular/common":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ngx-fluent_22.0.0_1782492177726_0.15218381952460658","host":"s3://npm-registry-packages-npm-production"}},"22.0.3":{"name":"@apappas1129/ngx-fluent","version":"22.0.3","description":"An Angular library for Fluent","author":{"name":"Alex Pappas","email":"apappas1129@gmail.com"},"contributors":[{"name":"Cerino Ligutom III","email":"zeferinix@gmail.com","url":"https://www.zeferinix.com"}],"license":"MIT","peerDependencies":{"@angular/common":"^22.0.0","@angular/core":"^22.0.0","@fluent/bundle":">= 0.18.0 < 1.0.0"},"dependencies":{"tslib":"^2.6.2"},"repository":{"type":"git","url":"git+https://github.com/apappas1129/ngx-fluent.git"},"homepage":"https://github.com/apappas1129/ngx-fluent#readme","keywords":["i18n","internationalization","l10n","localization","ftl","locale","language","translate","translation"],"module":"fesm2022/apappas1129-ngx-fluent.mjs","typings":"types/apappas1129-ngx-fluent.d.ts","exports":{"./package.json":{"default":"./package.json"},".":{"types":"./types/apappas1129-ngx-fluent.d.ts","default":"./fesm2022/apappas1129-ngx-fluent.mjs"}},"sideEffects":false,"type":"module","gitHead":"92fd77214e76979da99d5ad9d8c48039ec01fcb8","_id":"@apappas1129/ngx-fluent@22.0.3","bugs":{"url":"https://github.com/apappas1129/ngx-fluent/issues"},"_nodeVersion":"24.17.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-u9aRuRANJlwDNsBus9MkY4lXpDEDVXeJN5+Eaw4VWwZEGorZNWcqulk5sN0+rMogXitdpaGVu47s1gSr/BcQ0A==","shasum":"971012e8de5916a2288c7635c492ef60742991e1","tarball":"https://registry.npmjs.org/@apappas1129/ngx-fluent/-/ngx-fluent-22.0.3.tgz","fileCount":5,"unpackedSize":39111,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@apappas1129%2fngx-fluent@22.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH0fSqe8gXa2HxBtHHG44B6EFaHj9e8SM4f6tEOmejufAiAKmj53csGNSl2KFOXSdzcAoLMsmh49Zdv5QqDH9OkkAQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:2bd42a0e-b2a1-456c-8236-67c658320c7d"}},"directories":{},"maintainers":[{"name":"apappas1129","email":"apappas1129@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ngx-fluent_22.0.3_1782493389262_0.7374429511957332"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-26T16:42:57.249Z","modified":"2026-06-26T17:03:09.782Z","22.0.0":"2026-06-26T16:42:57.856Z","22.0.3":"2026-06-26T17:03:09.420Z"},"bugs":{"url":"https://github.com/apappas1129/ngx-fluent/issues"},"author":{"name":"Alex Pappas","email":"apappas1129@gmail.com"},"license":"MIT","homepage":"https://github.com/apappas1129/ngx-fluent#readme","keywords":["i18n","internationalization","l10n","localization","ftl","locale","language","translate","translation"],"repository":{"type":"git","url":"git+https://github.com/apappas1129/ngx-fluent.git"},"description":"An Angular library for Fluent","contributors":[{"name":"Cerino Ligutom III","email":"zeferinix@gmail.com","url":"https://www.zeferinix.com"}],"maintainers":[{"name":"apappas1129","email":"apappas1129@gmail.com"}],"readme":"# ngx-fluent\n\nAn [Angular](https://angular.io/) library for [Fluent](https://projectfluent.org/).\n\n## Angular compatibility\n\nLatest version available for each version of Angular\n\n | @apappas1129/ngx-fluent | Angular | @fluent/bundle |\n | --------------------- | ------- | -------------- |\n | >= 1.0                | 13.x    | < 1.x          |\n | >= 1.1                | 14.x    | < 1.x          |\n | >= 1.2                | 15.x    | < 1.x          |\n | >= 2.0                | 16.x    | < 1.x          |\n | >= 3.0                | 17.x    | < 1.x          |\n | >= 4.0                | 18.x    | < 1.x          |\n | >= 19.0               | 19.x    | < 1.x          |\n | >= 20.0               | 20.x    | < 1.x          |\n | >= 21.0               | 21.x    | < 1.x          |\n | >= 22.0               | 22.x    | < 1.x          |\n\n## Installation\n\n```bash\nnpm install --save @fluent/bundle @apappas1129/ngx-fluent\n```\n\n---\n\n## Setup — standalone apps (recommended)\n\nFor any Angular project using standalone bootstrap (`bootstrapApplication`).\n\n### 1. Register the library\n\nAdd `provideHttpClient()` and `provideNgxFluent()` to your `ApplicationConfig`. Specify `defaultLocale` to have the translation file fetched **before the root component renders** — no flash of untranslated keys on first paint:\n\n```ts\n// app.config.ts\nimport { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';\nimport { provideHttpClient } from '@angular/common/http';\nimport { provideNgxFluent } from '@apappas1129/ngx-fluent';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideBrowserGlobalErrorListeners(),\n    provideHttpClient(),\n    provideNgxFluent({\n      sources: {\n        en: 'assets/i18n/en.ftl',\n        sv: 'assets/i18n/sv.ftl',\n      },\n      defaultLocale: 'en',\n    }),\n  ],\n};\n```\n\n`provideHttpClient()` is a required prerequisite. The library uses Angular's `HttpClient` to fetch `.ftl` files but intentionally does not register it internally, so your app retains full control over interceptors and fetch configuration.\n\n### 2. Use the pipe\n\nImport `NgxFluentPipe` in any standalone component that needs translations:\n\n```ts\nimport { NgxFluentPipe } from '@apappas1129/ngx-fluent';\n\n@Component({\n  imports: [NgxFluentPipe],\n  template: `\n    <h1>{{ 'welcome-user' | fluent: { user: name } }}</h1>\n  `,\n})\nexport class MyComponent {\n  name = 'John Doe';\n}\n```\n\nThe pipe returns the key as-is if the translation cannot be resolved (locale not loaded, key missing).\n\n### 3. Deferred locale init from user preference\n\nOmit `defaultLocale` if you want to decide the initial locale at runtime — for example, from a value stored in `localStorage`:\n\n```ts\n// app.config.ts — no defaultLocale\nprovideNgxFluent({\n  sources: {\n    en: 'assets/i18n/en.ftl',\n    sv: 'assets/i18n/sv.ftl',\n  },\n}),\n```\n\n```ts\n// app.ts — set locale after app starts\nexport class App {\n  private fluent = inject(NgxFluentService);\n\n  constructor() {\n    const saved = localStorage.getItem('locale') ?? 'en';\n    this.fluent.setLocale(saved);\n  }\n}\n```\n\nThe root component renders before the first locale loads, so untranslated keys will briefly appear. Prefer `defaultLocale` when a hard-coded startup locale is acceptable.\n\n---\n\n## Setup — NgModule apps (legacy)\n\n> For projects that still bootstrap with `platformBrowserDynamic().bootstrapModule(AppModule)`. This pattern is fully supported; the library ships `NgxFluentModule` specifically for this case.\n\n### 1. Register the library\n\nImport `NgxFluentModule` into your root `AppModule`. It registers `HttpClient` internally with DI-based interceptor support, so no separate `HttpClientModule` is needed:\n\n```ts\n// app.module.ts\nimport { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { NgxFluentModule } from '@apappas1129/ngx-fluent';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    NgxFluentModule,\n  ],\n  bootstrap: [AppComponent],\n})\nexport class AppModule {}\n```\n\n### 2. Configure sources in your root component\n\nInject `NgxFluentService` and configure it in `ngOnInit`. Translated content appears after the first `ngOnInit` cycle rather than on first paint (unlike the standalone `defaultLocale` path):\n\n```ts\n// app.component.ts\nimport { Component, OnInit } from '@angular/core';\nimport { NgxFluentService } from '@apappas1129/ngx-fluent';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html',\n})\nexport class AppComponent implements OnInit {\n  constructor(private fluent: NgxFluentService) {}\n\n  ngOnInit() {\n    this.fluent.setTranslationSourceMap({\n      en: 'assets/i18n/en.ftl',\n      sv: 'assets/i18n/sv.ftl',\n    });\n    this.fluent.setLocale('en');\n  }\n}\n```\n\n### 3. Use the pipe\n\n`NgxFluentModule` exports the `fluent` pipe, making it available in all components declared in your `AppModule` — no per-component import needed:\n\n```html\n<h1>{{ 'welcome-user' | fluent: { user: name } }}</h1>\n```\n\nThe pipe returns the key as-is if the translation cannot be resolved.\n\n---\n\n## Translation source formats\n\nEach locale key in `sources` (or passed to `setTranslationSourceMap()`) accepts one of three formats:\n\n**URL string** — the library fetches the file and builds the bundle automatically:\n\n```ts\nsources: {\n  en: 'assets/i18n/en.ftl',\n}\n```\n\n**URL with bundle options** — same fetch, but you control the `FluentBundle` constructor config (e.g. disable Unicode isolation markers):\n\n```ts\nsources: {\n  en: {\n    path: 'assets/i18n/en.ftl',\n    bundleConfig: {\n      useIsolating: false,\n    },\n  },\n}\n```\n\n**Pre-built `FluentBundle` instance** — hand the library a bundle you constructed yourself. No HTTP fetch is performed:\n\n```ts\nimport { FluentBundle, FluentResource } from '@fluent/bundle';\n\nconst bundle = new FluentBundle('en', { useIsolating: false });\nbundle.addResource(new FluentResource('welcome-user = Welcome, {$user}!'));\n\nsources: {\n  en: bundle,\n}\n```\n\nThis gives you full control — useful for testing, SSR, or bundling translations at build time.\n\n*Tip: Make your locale keys compliant to the [BCP 47 standard](https://en.wikipedia.org/wiki/IETF_language_tag) to avoid issues with Fluent's built-in functions that rely on the `Intl` API.*\n\n---\n\n## Adding locales incrementally\n\nSources don't have to be declared all at once. Call `setTranslationSourceMap()` at any point to register additional locales — for example, when the user opens a language picker for the first time:\n\n```ts\n// On startup, register only the default\nthis.fluent.setTranslationSourceMap({ en: 'assets/i18n/en.ftl' });\nthis.fluent.setLocale('en');\n\n// Later, register more on demand\nthis.fluent.setTranslationSourceMap({ sv: 'assets/i18n/sv.ftl', fr: 'assets/i18n/fr.ftl' });\n```\n\nPassing a locale key that is already loaded causes its translations to be reloaded from the new source.\n\n---\n\n## Switching locale\n\nCall `setLocale()` on `NgxFluentService` at any time. The service fetches the translation file if not already cached, then all active `fluent` pipes update reactively:\n\n```ts\n// standalone\nprivate fluent = inject(NgxFluentService);\n\n// NgModule\nconstructor(private fluent: NgxFluentService) {}\n\n// both\nthis.fluent.setLocale('sv');\n```\n\nSwitching to a previously loaded locale is instantaneous — files are cached in memory after the first fetch.\n\n---\n\n## Translating programmatically\n\n`translate()` is synchronous and reads directly from the currently loaded bundle:\n\n```ts\n// standalone\nprivate fluent = inject(NgxFluentService);\n\n// NgModule\nconstructor(private fluent: NgxFluentService) {}\n\n// both\ngetLabel(key: string) {\n  return this.fluent.translate(key, { user: 'John' }) ?? key;\n}\n```\n\nReturns `null` if the locale is not loaded or the key is not found. The `?? key` fallback mirrors what the pipe does automatically.\n\n---\n\n## Example projects\n\nThis repository ships two example projects.\n\n**`ngx-fluent-example-standalone`** — a modern Angular 22 standalone app. Zoneless (no `zone.js`), signal-based component state, `OnPush` change detection by default, `provideNgxFluent` in `app.config.ts`. Reference implementation for new projects consuming the library today.\n\n**`ngx-fluent-example`** — an Angular 22 project intentionally structured with `AppModule` and `platformBrowserDynamic` bootstrap. For teams that cannot yet migrate to standalone — demonstrates that `NgxFluentModule` provides the same behavior under the NgModule pattern. Retains `zone.js`, `ChangeDetectionStrategy.Eager`, and constructor injection, all of which are correct for that pattern.\n\n---\n\n## Contributing\n\nSee [Contributing Guide](/CONTRIBUTING.md).\n","readmeFilename":"README.md"}