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

@rerune/angular

v1.6.0

Published

Native Angular OTA translation adapters for ngx-translate and Transloco.

Readme

@rerune/angular

@rerune/angular is the Angular binding layer for ReRune's OTA runtime, with native ngx-translate and Transloco integrations.

This guide targets SDK 1.5.0. Combined native providers are available since 1.4.0.

Requirements

  • Angular 18 or newer and RxJS 7.
  • An application using ngx-translate 18 or Transloco 8, configured normally.
  • Bundled translations and any native loader/compiler plugins your app needs.
  • A publishable/read-only OTA publish ID for browser runtime fetches.

Install only the engine your app uses. Both adapters ship in the same package, but neither requires the other engine:

npm install @rerune/[email protected] @ngx-translate/core@18

Or, for a Transloco application:

npm install @rerune/[email protected] @jsverse/transloco@8

Use @rerune/angular/ngx-translate or @rerune/angular/transloco for setup and service imports. The package root does not export a setup API. Older engine majors require an upgrade; see Compatibility Evidence for tested Angular versions.

Primary Integration Path

For most apps, the intended public path is:

  1. Keep your existing translation engine configuration and loaders.
  2. Replace its root provider call with ReRune.provide({ otaPublishId }, nativeOptions), passing the same native options as the second argument.
  3. Keep using native pipes, directives, services, language switching, and bundled loaders.

ReRune handles OTA fetch, cache, merge, and update notification. It does not add a ReRune-specific translation pipe or t() wrapper. Register exactly one adapter, once, at the application root. Do not add it to lazy routes or child translation providers.

Browser Setup

ReRune.provide(...) registers the selected native engine internally when you pass its configuration as the second argument. You do not also call provideTranslateService(...) or provideTransloco(...). Keep your loader and language settings once in that native configuration.

Existing applications keep their loaders, parsers, compilers, plugins, and fallback configuration. The second argument has the native provider's own TypeScript type. Replace only the root translation provider call, keeping your zone/zoneless, routing, hydration, and separate plugin providers in place.

Each comparison starts with an app using its native engine without ReRune, not an older ReRune SDK. Choose your engine, then replace its "Before" setup with "After". Do not combine both alternatives.

Bundled Translations

This in-memory loader works with either example. It stands in for your existing JSON/HTTP loader; you do not need to move translations into TypeScript to use ReRune.

If your base resources require HTTP, their offline availability remains your loader's responsibility. ReRune does not cache those loader responses.

// translations.ts
import { Injectable } from '@angular/core'
import { of } from 'rxjs'

const resources: Record<string, Record<string, string>> = {
  en: {
    headline: 'Bundled headline',
    greeting: 'Hello, {{name}}',
  },
  de: {
    headline: 'Gebuendelte Ueberschrift',
    greeting: 'Hallo, {{name}}',
  },
}

@Injectable()
export class BundledLoader {
  getTranslation(language: string) {
    return of(resources[language] ?? {})
  }
}

ngx-translate

Before ReRune

// app.config.ts (ngx-translate)
import { ApplicationConfig } from '@angular/core'
import { provideTranslateService, TranslateLoader } from '@ngx-translate/core'
import { BundledLoader } from './translations'

export const appConfig: ApplicationConfig = {
  providers: [
    provideTranslateService({
      lang: 'en',
      fallbackLang: 'en',
      loader: { provide: TranslateLoader, useClass: BundledLoader },
    }),
  ],
}

After ReRune

// app.config.ts (ngx-translate)
import { ApplicationConfig } from '@angular/core'
import { TranslateLoader } from '@ngx-translate/core'
import { ReRune } from '@rerune/angular/ngx-translate'
import { BundledLoader } from './translations'

export const appConfig: ApplicationConfig = {
  providers: [
    ReRune.provide({
      otaPublishId: '<READ_ONLY_OTA_PUBLISH_ID>',
      supportedLocales: ['en', 'de'],
    }, {
      lang: 'en',
      fallbackLang: 'en',
      loader: { provide: TranslateLoader, useClass: BundledLoader },
    }),
  ],
}

Replace the provideTranslateService import and call with ReRune. The native options object moves unchanged to the second argument. Keep TranslateLoader and your loader class. Do not register provideTranslateService(...) separately when passing native options to ReRune.

supportedLocales is needed here because the loader knows about German, but ngx-translate has not registered that language yet. Supply this hint for unregistered lazy-loaded root languages; it is not a second language-selection setting. Transloco exposes its language list natively, as shown below.

ngx-translate 18 uses provider functions; older TranslateModule.forRoot(...) examples do not apply to this supported engine version.

Transloco

Before ReRune

// app.config.ts (Transloco)
import { ApplicationConfig } from '@angular/core'
import { provideTransloco } from '@jsverse/transloco'
import { BundledLoader } from './translations'

export const appConfig: ApplicationConfig = {
  providers: [
    provideTransloco({
      config: {
        availableLangs: ['en', 'de'],
        defaultLang: 'en',
        fallbackLang: 'en',
        reRenderOnLangChange: true,
      },
      loader: BundledLoader,
    }),
  ],
}

After ReRune

// app.config.ts (Transloco)
import { ApplicationConfig } from '@angular/core'
import { ReRune } from '@rerune/angular/transloco'
import { BundledLoader } from './translations'

export const appConfig: ApplicationConfig = {
  providers: [
    ReRune.provide({
      otaPublishId: '<READ_ONLY_OTA_PUBLISH_ID>',
    }, {
      config: {
        availableLangs: ['en', 'de'],
        defaultLang: 'en',
        fallbackLang: 'en',
        reRenderOnLangChange: true,
      },
      loader: BundledLoader,
    }),
  ],
}

Replace the provideTransloco import and call with ReRune. Its native config and loader move unchanged to the second argument, with no extra ReRune language list. Do not also register provideTransloco(...).

Keep separate plugins, such as provideTranslocoMessageformat(), after the combined provider just as they followed the native provider. ReRune does not install or replace those plugins.

The engine owns the active language. ReRune derives configured languages from the engine. For ngx-translate, list additional lazy-loaded root languages in supportedLocales if the engine has not registered them yet. This lets ReRune distinguish app-owned loader languages from dashboard-only languages. Transloco normally provides this information through availableLangs.

Browser startup does not wait for OTA network delivery. The app can render bundled translations while ReRune restores its cache and checks for updates. The default browser store uses localStorage and falls back to memory when storage is unavailable or a write fails. No custom cache is needed for normal setup.

No custom fetch, interceptor, or response decoder is required for the hosted service. The manifest base is fixed internally to https://rerune.io/api; there is no public backend URL override. Requests use X-OTA-Publish-Id for ReRune identity.

Variants And Update Options

Add options to the same provider, not a second registration:

ReRune.provide({
  otaPublishId: 'publishable-read-id',
  variant: 'vip',
  updatePolicy: { checkOnStart: true, periodicIntervalInHours: 24 },
  logLevel: 'info',
}, nativeOptions)

variant defaults to ReRune.Main. Use a lowercase published slug for audience-specific wording of the same key and language. Missing overrides fall back to Main content. Variants are separate from language selection and plural grammar. See the translation variants guide for the dashboard workflow.

The browser store restores a persisted selection before applying cached translations. That value wins over the provider's variant option, including a persisted Main. Use ReRuneService.setVariant(...) when the selection becomes known later.

Console logging defaults to off; structured errors and warnings remain available. Levels error, info, and verbose are cumulative. verbose can expose the OTA publish ID, translation payloads, and error details; enable it only when that disclosure is acceptable.

Angular Usage

Translation code stays the same before and after setup: keep existing pipes, directives, services, and language-switch calls. The examples below add optional status and refresh controls; those additions are not required for translations to update. Inject ReRuneService only when a component needs those controls.

ngx-translate

// app.component.ts (ngx-translate)
import { Component, inject } from '@angular/core'
import { TranslatePipe, TranslateService } from '@ngx-translate/core'
import { ReRuneService } from '@rerune/angular/ngx-translate'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TranslatePipe],
  template: `
    <h1>{{ 'headline' | translate }}</h1>
    <p>{{ 'greeting' | translate: { name: 'Ada' } }}</p>
    <button (click)="translations.use('de').subscribe()">Deutsch</button>
    <button (click)="rerune.checkForUpdates()">
      Refresh ({{ rerune.state().status }})
    </button>
  `,
})
export class AppComponent {
  readonly translations = inject(TranslateService)
  readonly rerune = inject(ReRuneService)
}

Transloco

// app.component.ts (Transloco)
import { Component, inject } from '@angular/core'
import { TranslocoPipe, TranslocoService } from '@jsverse/transloco'
import { ReRuneService } from '@rerune/angular/transloco'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TranslocoPipe],
  template: `
    <h1>{{ 'headline' | transloco }}</h1>
    <p>{{ 'greeting' | transloco: { name: 'Ada' } }}</p>
    <button (click)="translations.setActiveLang('de')">Deutsch</button>
    <button (click)="rerune.checkForUpdates()">
      Refresh ({{ rerune.state().status }})
    </button>
  `,
})
export class AppComponent {
  readonly translations = inject(TranslocoService)
  readonly rerune = inject(ReRuneService)
}

Native directives work too. ReRune enables Transloco's reRenderOnLangChange while the adapter is active so existing views update after same-language OTA changes.

For a published cardinal plural such as cartItems, pass { count: 3 } through the normal pipe or service arguments. OTA plurals do not require a MessageFormat plugin. Bundled ICU strings still need your engine's native compiler/plugin; ReRune does not install one.

Within each locale, the merge order is:

  1. Application translations, including late root-loader results and native catalog writes.
  2. Remote OTA values for matching keys.

An active-language application value wins over a fallback-language OTA value. Missing keys can fall back through the parent language, manifest main language, and the engine's configured fallback behavior. Removing an OTA override restores the latest application value. An explicit empty published string remains an override; an untranslated record contributes no override.

rerune.state() is a read-only Angular signal. Its availableLocales and localeNames expose configured plus dashboard-published languages and their display names. Every published locale is synchronized, not just the current language. Keep using the engine's normal language-switch method.

Optional Runtime Controls

Inside an async method of a component or service that injects ReRuneService as rerune:

await this.rerune.ready()
const result = await this.rerune.checkForUpdates()
if (result.hasErrors) {
  console.error('Translation refresh failed', result.errors)
}

await this.rerune.setVariant('vip')
await this.rerune.setVariant({ variant: 'vip', persist: true })
await this.rerune.setVariant()
await this.rerune.resetVariant()

ready() waits for initial cache hydration and the configured startup check. It is not a guarantee that every app loader or OTA request succeeded. setVariant() selects Main for the current runtime without changing the stored choice. resetVariant() selects and persists Main. Effective changes rebuild cached overlays immediately and update rerune.state().variant.

Removing ReRune

Replace ReRune.provide(otaOptions, nativeOptions) with your engine's original provideTranslateService(nativeOptions) or provideTransloco(nativeOptions). Restore its native import and keep the same root configuration, loader, compiler/parser, and separately registered plugins. Pipes, directives, and native language-switch calls stay unchanged.

Remove optional ReRuneService status/refresh/variant controls if you added them, then uninstall @rerune/angular. OTA delivery stops and ReRune's cache is no longer consumed, so keep the messages you need in native bundles/loaders. This is a code migration followed by an app restart, not a runtime provider toggle.

Startup, Offline, And Partial Updates

Offline startup is supported through the normal root ReRune.provide(...) setup:

  1. Browser bootstrap does not wait for OTA. The app can render its native bundled translations as they become available.
  2. ReRune reads browser storage asynchronously and overlays matching cached translations. The first render may show bundled copy while this read finishes.
  3. After cache restoration, the default startup check fetches OTA updates without blocking app startup. Network failures leave cached or bundled copy available.
  4. Valid locale updates are written through the cache store before becoming active in memory. Changed resources notify the native engine, so its reactive pipes/subscriptions can refresh displayed text.

You do not need to await ReRuneService.ready() to enable this browser flow. SSR uses transferred data first and defers cache/network work until hydration readiness. Server rendering intentionally waits for initialization, as described below.

The default browser store persists data across reloads when storage works. Its write-failure fallback lasts only in memory. Cache writes and { persist: true } are best effort, with no background persistence retry. Dashboard-only languages need an earlier successful manifest and locale fetch to be available offline; bundled languages do not. An app-owned HTTP loader still needs its own offline strategy if its base resources are not bundled or otherwise cached; ReRune does not cache those loader responses.

Updates are independent per language. If English succeeds while German fails, English can use the new copy and German keeps its previously committed copy (or native fallback if none exists). This is intentional. Inspect rerune.state().lastResult or the result of checkForUpdates() for errors.

SSR And SEO Preload

Browser-only OTA cannot update HTML already sent to a crawler. Keep Angular's normal server rendering and hydration configuration, with the same root ReRune provider and native translation setup on server and browser. Unlike React, Angular does not need a separate ReRune.preload(...) call.

On the server, the provider waits for its configured initial OTA check and writes canonical translation payloads, the selected variant, and main language into Angular TransferState. The browser consumes this state synchronously before asynchronous cache restoration and synchronization. Keep startup checks enabled when server-rendered OTA copy is required.

The browser waits for the first root bootstrap before starting cache restoration or network work, so persisted preferences cannot replace the transferred copy before that first render. A stored variant may change the text afterward. Normal eager hydration needs no extra setup.

For incremental/deferred hydration, pass hydrationReady: Promise<void> to ReRune.provide(...) and resolve it after all server-rendered translation consumers hydrate. First root bootstrap alone does not establish that. The promise is browser-only and ignored during server rendering. An unresolved promise holds asynchronous cache reads and checks; rejection reports an error without starting them. Keep explicit language/variant changes after hydration as well.

Translate SEO-visible values after initialization in your server rendering path. A title or description computed once before translations load is not recalculated automatically. Root translation loaders must finish for rendering to settle. If you customize server bootstrap, pass Angular's supplied bootstrap context to bootstrapApplication(...) as required by your Angular version.

Server caches default to request-local memory. If you supply a custom store, own its request/project isolation and never share mutable user-specific state across unrelated requests. The built-in browser cache is scoped by publish ID and cache schema; custom stores must implement their own isolation and optional variant persistence methods.

Previewing Draft Translations

Pass staging: true to ReRune.provide(...) from either engine entry point to preview enabled draft translations while project staging is enabled in the dashboard. Every synchronization then fetches the complete draft snapshot of each manifest locale and replaces the stored document, so deleted or disabled keys disappear and never-published keys appear. Manifest versions do not track draft edits, so staging mode checks every locale on every sync, including after a manifest 304. Staging data uses a separate cache scope, and returning to production through await ReRuneService.setStaging(false) restores the production cache before synchronizing published content. Use ReRuneService.setStaging(true) to switch back and ReRuneService.isStaging() to report the current mode. The staging provider option selects only the initial mode.

Server and browser configuration must use the same mode. TransferState records the server mode, and the browser discards a mismatched snapshot before it can supply translations.

Known Limitations

ReRuneService exposes structured omission diagnostics, including Angular path conflicts. state().warnings describes the current selection; lastResult and manual results retain the last check's snapshot. hasWarnings does not mean hasErrors, and console logging is not required.

const result = await this.rerune.checkForUpdates()
for (const warning of result.warnings) {
  console.warn(warning.locale, warning.key, warning.reasons)
}

The advanced client property exposes core catalog diagnostics only. Use the service state/results when investigating Angular-specific projection omissions.

  • Root catalog only. OTA does not target ngx-translate child catalogs, Transloco named scopes, or backend namespaces. Those remain application-owned. Do not publish scoped keys expecting scoped delivery.
  • No Angular localize adapter. @angular/localize, extracted Angular message IDs, XLIFF delivery, and structural placeholders such as {$START_TAG_SPAN} are unsupported. No compatibility layer is included.
  • Limited OTA message grammar. Published messages support plain text, declared named interpolation, and one count cardinal plural with an optional exact-zero form. General ICU select, ordinal or nested plurals, rich-template structures, and runtime argument-type validation are not provided. Existing bundled ICU still uses the native engine plugin. Custom compilers/parsers/transpilers do not extend ReRune's OTA grammar.
  • Reactive consumers only. Native reactive pipes/signals can update. Strings saved from TranslateService.instant(...), TranslocoService.translate(...), or one-shot subscriptions do not update themselves. Re-read them or use the engine's reactive API. Static HTML, generated metadata, and already-sent SSR responses need another render/build to change.
  • Dotted-key conflicts. OTA keys use dot paths. Empty path segments, reserved segments (__proto__, constructor, prototype), and conflicting keys such as both account and account.title are omitted from the Angular overlay. There is no configurable key separator or literal-dot mode. These omissions appear in state().warnings and update-result warnings.
  • Check-based delivery. Startup, relevant language changes, manual refresh, and configured timers trigger asynchronous checks. These requests do not block browser bootstrap, but they are not push delivery or an OS background task. A closed page cannot keep checking. All manifest languages are synchronized; supportedLocales is not a remote-download filter.
  • Invalid updates retain fallback. Structural payload errors or rejected custom locale writes preserve that locale's previous copy. Placeholder-conflicting keys are omitted instead of invalidating the locale. See startup, offline, and partial updates for the supported update policy.
  • Variants share project resource access. Anyone able to read delivered project resources can inspect their included variants, including those in cache or hydration data. Selecting a variant changes wording, not read permissions.

Advanced APIs

Existing Root Registration

If a shared module must keep its existing native root provider, register ReRune.provide({ otaPublishId }) once alongside it, without a second argument. ReRune uses that configured engine rather than registering another one. This is an alternative for existing integrations, not an extra step in the Quick Start.

Most apps only need ReRune.provide(...). ReRuneService.client exposes the lower-level catalog client for custom integrations, and ReRuneAngularOptions describes provider options. Import ReRuneService and ReRuneAngularOptions from the selected engine entry point.

For custom persistence, createReRuneBrowserCacheStore(...) and the ReRuneCacheStore contract are available from @rerune/core. A rejected custom locale write keeps that locale's previous committed translations active.

Runnable Examples

Choose the public example for your existing translation engine:

Both install published npm packages, use native pipes and a MessageFormat plugin for bundled ICU, and match the React web example's look and feel. Each guide covers startup, ports, and a manual checklist. Use the public demo project or your own publish ID. No access to the SDK source repository is required.

Compatibility Evidence

The 1.5.0 candidate passed packed runtime/zoneless SSR and consumer type checks on Angular 18 through 22, including single-engine installs on 22. Declaration checking uses Angular's moduleResolution: "Bundler" with skipLibCheck: false. NodeNext consumer checks use skipLibCheck: true because Transloco 8.4.0's @jsverse/utils declarations contain extensionless ESM imports. The same strict NodeNext errors reproduce when importing only native Transloco, without ReRune. Native provider types are preserved rather than copied into a separate SDK schema.

The 1.5.0 candidate checks tested packed imports, runtime behavior with mocked OTA, zoneless SSR, native pipes, and TypeScript resolution on Angular 18.2.14, 19.2.25, 20.3.30, 21.2.22, and 22.1.5. Browser-startup regression tests used Angular 18 with jsdom; production example builds also used Angular 18. Public example browser checks were not rerun for this candidate. Permitting future Angular versions to install is not a compatibility guarantee.

Safari/Firefox, full production SSR-to-browser DOM hydration, and every third-party loader/compiler/parser/interceptor or NgModule arrangement have not been exercised.

License

The ReRune SDK is available under the MIT License. You may use, copy, modify, and redistribute it, including commercially, subject to the license notice requirement. The license covers the published SDK packages; separately operated ReRune backend and hosted services are governed by their own terms. The complete license text and copyright notice are included in each package's LICENSE file.