npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@flusys/ng-localization

v4.1.1

Published

Localization module for multi-language support in FLUSYS Angular apps

Readme

@flusys/ng-localization

Database-driven internationalization (i18n) library for the FLUSYS Angular platform — multi-language support, RTL/LTR, TranslatePipe, language selector, and Angular APP_INITIALIZER bootstrap.

npm version Angular TypeScript License: MIT


Table of Contents


Overview

@flusys/ng-localization provides database-driven i18n for FLUSYS applications. Translations are stored in the backend (nestjs-localization) and loaded at app startup via APP_INITIALIZER. The TranslatePipe integrates with TranslateService through the TRANSLATE_ADAPTER injection token — keeping all other packages independent of ng-localization.

Unlike file-based i18n (ngx-translate), FLUSYS translations are managed in the database and can be updated at runtime without rebuilding.


Features

  • ✅ Database-driven translations (updated without rebuild)
  • TranslatePipe with signal reactivity — re-renders on language switch
  • {{variableName}} interpolation with messageVariables
  • ✅ RTL/LTR document direction auto-switch
  • ✅ APP_INITIALIZER bootstrap — translations loaded before app renders
  • ✅ Route resolver for per-route language loading
  • ✅ Language selector component for topbar
  • provideLocalization() — registers TRANSLATE_ADAPTER and LAYOUT_LANGUAGE_SELECTOR
  • ✅ Missing key detection and fallback

Compatibility

| Package | Version | |---------|---------| | Angular | 21+ | | @flusys/ng-core | 4.x | | @flusys/ng-shared | 4.x |


Installation

npm install @flusys/ng-localization @flusys/ng-core @flusys/ng-shared

Quick Start

1. Enable Localization in Config

// environments/environment.ts
export const environment = {
  services: {
    localization: { enabled: true },
  },
};

2. Register Localization Providers

// app.config.ts
import { provideLocalization } from '@flusys/ng-localization';

export const appConfig: ApplicationConfig = {
  providers: [
    ...provideLocalization(),
    // Provides:
    // - TRANSLATE_ADAPTER (used by TranslatePipe, ng-shared)
    // - LAYOUT_LANGUAGE_SELECTOR (used by AppTopbar from ng-layout)
    // - APP_INITIALIZER (loads translations before app renders)
  ],
};

3. Use TranslatePipe in Templates

<!-- Basic translation -->
<span>{{ 'common.save' | translate }}</span>
<button>{{ 'common.cancel' | translate }}</button>
<h1>{{ 'dashboard.title' | translate }}</h1>

<!-- With parameters -->
<p>{{ 'pagination.showing' | translate: { from: 1, to: 10, total: 100 } }}</p>

Module Registration

provideLocalization()

import { provideLocalization } from '@flusys/ng-localization';

providers: [
  ...provideLocalization(),
]

Registers:

  • TRANSLATE_ADAPTERTranslateService implementation (used by TranslatePipe from ng-shared)
  • LAYOUT_LANGUAGE_SELECTORILanguageSelectorProvider implementation (used by AppTopbar)
  • APP_INITIALIZER — loads translations for the default language before first render

TranslateService

The core service managing translations and language state:

import { TranslateService } from '@flusys/ng-localization';

@Component({ ... })
export class MyComponent {
  private translate = inject(TranslateService);

  // Signals
  currentLanguage = this.translate.currentLanguage;  // Signal<ILanguage>
  availableLanguages = this.translate.languages;     // Signal<ILanguage[]>
  isLoading = this.translate.isLoading;              // Signal<boolean>

  // Methods
  translate(key: string, params?: Record<string, string | number>): string {
    return this.translate.get(key, params);
  }

  switchLanguage(languageCode: string): void {
    this.translate.setLanguage(languageCode);
  }
}

TranslateService API:

| Member | Type | Description | |--------|------|-------------| | currentLanguage | Signal<ILanguage> | Active language object | | languages | Signal<ILanguage[]> | All available languages | | isLoading | Signal<boolean> | True when switching languages | | get(key, params?) | string | Translate a key synchronously | | setLanguage(code) | Observable<void> | Switch language (loads translations) | | reloadTranslations() | Observable<void> | Force reload from backend |

ILanguage Model:

interface ILanguage {
  id: string;
  name: string;           // "English"
  nativeName: string;     // "English" / "العربية"
  code: string;           // "en" / "ar"
  isDefault: boolean;
  isRtl: boolean;
  isActive: boolean;
  flagIcon?: string;      // Country flag identifier
}

TranslatePipe

The TranslatePipe from @flusys/ng-shared is powered by TRANSLATE_ADAPTER. It is signal-reactive — when the language changes, all pipes re-render automatically.

<!-- Basic key -->
{{ 'menu.dashboard' | translate }}

<!-- With interpolation parameters -->
{{ 'user.greeting' | translate: { name: userName() } }}

<!-- In attribute binding -->
<input [placeholder]="'form.search.placeholder' | translate" />

<!-- In event handler label -->
<button [title]="'action.delete.tooltip' | translate">
  <i class="pi pi-trash"></i>
</button>

Translation Key Convention:

module.component.element

Examples:

common.save
common.cancel
common.delete
auth.login.title
auth.login.email.label
dashboard.title
menu.products
pagination.showing

Variable Interpolation

Use {{variableName}} in translation values stored in the database:

# In database:
Key: "pagination.showing"
Value: "Showing {{from}}-{{to}} of {{total}} results"

# In Arabic:
Key: "pagination.showing"
Value: "عرض {{from}}-{{to}} من {{total}} نتيجة"
<!-- Angular template -->
{{ 'pagination.showing' | translate: { from: page * pageSize + 1, to: endItem, total: total() } }}

Backend messageVariables field:

When the backend sends translation responses, it includes a messageVariables field with the variable names the translation expects:

{
  "key": "pagination.showing",
  "value": "Showing {{from}}-{{to}} of {{total}} results",
  "messageVariables": ["from", "to", "total"]
}

Language Management

Languages are managed via the ng-localization admin pages:

// app.routes.ts
import { LOCALIZATION_ROUTES } from '@flusys/ng-localization';

export const routes: Routes = [
  {
    path: 'localization',
    loadChildren: () => LOCALIZATION_ROUTES,
  },
];

| Route | Description | |-------|-------------| | /localization/languages | Manage languages (add, enable, set RTL) | | /localization/translations | Manage translation keys and values | | /localization/translations/:module | Filter by module |

TranslateApiService for programmatic access:

import { TranslateApiService } from '@flusys/ng-localization';

@Injectable({ ... })
export class MyService {
  private translateApi = inject(TranslateApiService);

  getLanguages(): Observable<IListResponse<ILanguage>> {
    return this.translateApi.getLanguages();
  }

  getTranslations(languageCode: string): Observable<Record<string, string>> {
    return this.translateApi.getTranslations(languageCode);
  }
}

Components

LanguageSelectorComponent

Language switcher dropdown. Registered via LAYOUT_LANGUAGE_SELECTOR and rendered automatically in AppTopbar.

<!-- Rendered automatically in AppTopbar when provideLocalization() is called -->
<!-- Manual embedding: -->
<flusys-language-selector />

Features:

  • Shows current language flag + name
  • Dropdown lists all active languages
  • Switches language and reloads translations on selection

Route Resolver

Use the route resolver to ensure translations are loaded before a route renders:

import { localizationResolver } from '@flusys/ng-localization';

// app.routes.ts
export const routes: Routes = [
  {
    path: '',
    resolve: { translations: localizationResolver },
    children: [
      // Routes that need translations pre-loaded
    ],
  },
];

The resolver calls TranslateService.reloadTranslations() if translations are stale or not yet loaded for the current language.


RTL Support

When a user switches to an RTL language (e.g., Arabic, Hebrew), TranslateService automatically updates the document direction:

// Internal behavior (automatic)
document.documentElement.dir = language.isRtl ? 'rtl' : 'ltr';
document.documentElement.lang = language.code;

Tailwind CSS RTL classes (rtl:, ltr:) work automatically once the dir attribute is set.

PrimeNG also supports RTL via the dir attribute on the document root — no extra configuration needed.


TRANSLATE_ADAPTER Token

TRANSLATE_ADAPTER is defined in @flusys/ng-core. All packages that use TranslatePipe or need translation access inject this token — not TranslateService directly. This keeps them independent of ng-localization.

// Defined in ng-core
import { TRANSLATE_ADAPTER, ITranslateAdapter } from '@flusys/ng-core';

// ITranslateAdapter interface
interface ITranslateAdapter {
  translate(key: string, params?: Record<string, string | number>): string;
  currentLanguage: Signal<ILanguage>;
  setLanguage(code: string): Observable<void>;
}

// ng-localization provides the implementation:
// provideLocalization() → { provide: TRANSLATE_ADAPTER, useClass: TranslateService }

API Endpoints

| Method | Endpoint | Description | |--------|----------|-------------| | POST | /localization/language/get-all | List all languages | | POST | /localization/language/get/:id | Get language by ID | | POST | /localization/language/insert | Add language | | POST | /localization/language/update | Update language | | POST | /localization/language/delete | Delete language | | POST | /localization/translation/get-all | List translations | | POST | /localization/translation/get-by-language | Get all keys for a language | | POST | /localization/translation/insert | Add translation key | | POST | /localization/translation/update | Update translation value | | POST | /localization/translation/delete | Delete translation key | | POST | /localization/translation/bulk-update | Atomic bulk update |


Configuration Reference

| Config Key | Type | Default | Description | |------------|------|---------|-------------| | services.localization.enabled | boolean | false | Enable localization module |

Default language is configured server-side in nestjs-localization via ILanguage.isDefault.


Troubleshooting

TranslatePipe shows raw key (e.g., common.save)

Possible causes:

  1. provideLocalization() is missing from app.config.ts
  2. The key doesn't exist in the database for the current language
  3. Translations failed to load — check network tab for POST /localization/translation/get-by-language

APP_INITIALIZER delays app startup

Translations are loaded before the first render. For large translation sets, this adds startup time. Consider splitting translations by module and using the route resolver for lazy loading.

Language switch doesn't update all components

TranslatePipe is signal-reactive. If a component uses raw translate.get() (synchronous call, not pipe), it won't auto-update. Use {{ key | translate }} in templates instead.

RTL layout not applying

Ensure Tailwind CSS is configured for RTL classes. Add to tailwind.config.js:

module.exports = {
  // Tailwind reads dir from document root automatically
  // No extra config needed for rtl: classes
};

No provider for TRANSLATE_ADAPTER

Add provideLocalization() to app.config.ts providers.

Missing translations after adding new keys

Call TranslateService.reloadTranslations() or navigate away and back (triggers the route resolver). In-memory translations are cached until the next load.


License

MIT © FLUSYS