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

@ayinza_tech/i18n-config

v1.2.6

Published

Shared i18next configuration for multiple portals

Readme

ayinza-i18n-config

Shared i18next configuration and formatting utilities for Ayinza portals.

This package centralizes i18n initialization, sensible defaults, and a set of formatters (currency, number, percent, date/time, relative time) that are integrated with i18next's interpolation system. It also provides React hooks for easy consumption in React apps.

Table of contents

  • About
  • Installation
  • Quick start
  • API
  • Configuration
  • Examples
  • Translation extraction
  • Testing
  • Development
  • License

About

The library bundles a default i18n configuration (detection, backend, namespaces, and formatters) and exposes helpers to initialize i18next, access the global i18n instance, and consume localization + formatting helpers in React components.

It is intentionally lightweight and designed to be used as a shared dependency across multiple portals that want consistent localization behavior.

Installation

Install the package and peer dependencies (peer dependencies are required by consumers and not bundled):

# Using npm
npm install @ayinza_tech/i18n-config i18next react react-i18next

# Using yarn
yarn add @ayinza_tech/i18n-config i18next react react-i18next

Also install the optional runtime backends used by this package (the package declares them as dependencies):

npm install i18next-http-backend i18next-browser-languagedetector

Note: This package declares i18next, react, and react-i18next as peerDependencies — install versions compatible with your app.

Quick start

Initialize i18n at application startup (for example in src/main.tsx or src/index.tsx in a React app):

import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { initializeI18n } from "@ayinza_tech/i18n-config";

// Optional: pass overrides to customize backend, supported languages, or
// formatters.
initializeI18n({
  config: {
    portalName: "My Portal",
    backend: {
      loadPath: "/locales/{{lng}}/{{ns}}.json",
      customHeaders: { "X-Portal": "my-portal" },
    },
    supportedLngs: ["en", "ar", "fr"],
  },
})
  .then(() => {
    const root = createRoot(document.getElementById("root")!);
    root.render(<App />);
  })
  .catch((err) => console.error(err));

The function returns the initialized i18next instance and also registers a set of custom formatters so you may use them inside translations (interpolation formatters) or via the provided hooks.

SSR note: initializeI18n touches browser-only globals to set the dir attribute. The implementation now guards those calls, but you should still run initialization on the client (e.g., inside a useEffect or Next.js use client entry point) to ensure detectors and DOM updates work.

API

Top-level exports (from src/index.ts):

  • initializeI18n(options?: I18nInitOptions): Promise<i18n> — initialize the i18next instance with defaults merged with your overrides.
  • getI18nInstance(): i18n — access the i18next singleton.
  • getFormatters(): I18nFormatters — get the formatter instance (throws if not initialized).
  • defaultConfig — the default configuration object used by initializeI18n.
  • createI18nConfig(partial?: Partial<I18nConfig>) — returns a fully merged config object without initializing i18next; useful for building configs in build-time tooling or sharing defaults across portals.
  • createTranslationSnapshot(options: CreateTranslationSnapshotOptions) — flatten translation JSON trees into a comparable snapshot for diffing.
  • collectNewTranslationKeys(options: CollectNewTranslationKeysOptions) — compute keys that were added between two snapshots.
  • handleNewTranslationKeys(options: HandleNewTranslationKeysOptions) — push detected keys to a remote endpoint or log them during dry runs.
  • useFormatting() — React hook providing formatting helpers bound to the current language.
  • useI18n() — combined hook returning both useTranslation() props and formatting helpers.
  • Both hooks expect initializeI18n to have completed; call initialization in your app bootstrap before rendering components that use them, otherwise getFormatters() will throw.
  • Re-exports: useTranslation, Trans, Translation from react-i18next.

Types exported (from src/types.ts):

  • I18nConfig — top-level configuration object shape.
  • I18nInitOptions — options for initializeI18n.
  • FormattersConfig, LocaleMapping — configuration shapes for formatters.

Formatters class: I18nFormatters provides methods such as:

  • formatCurrency(amount, language, currency?, options?)
  • formatNumber(value, language, options?)
  • formatPercent(value, language, options?)
  • formatDate(date, language, options?)
  • formatTime(date, language, options?)
  • formatDateTime(date, language, options?)
  • formatRelativeTime(value, unit, language, options?)

These are already wired into i18next as interpolation formatters named currency, number, percent, date, time, datetime, and relative.

Every formatter catches Intl errors and falls back to simple strings (for example, returning INVALID 100 for a bad currency code or toLocaleString() for an invalid date). This keeps your UI from crashing, but you may still see console warnings when supplying malformed input.

Configuration

defaultConfig (summary):

  • backend: { loadPath }
  • detection: browser language detection configuration
  • fallbackLng: en
  • supportedLngs: ["en","ar","fr","es"] (override to match your portal to avoid loading unused bundles)
  • defaultNS / ns: namespaces used (feel free to switch to common if that is your primary namespace)
  • interpolation.escapeValue: false
  • formatters: default formatter configuration (defaultCurrency USD, fallbackLocale: "en-US" when no locale mapping matches)
  • react: { useSuspense: true } but you can extend it with bindI18n/bindI18nStore to match your React rendering mode

You can override only the pieces you need — initializeI18n merges defaults with your partial config. If you need a pure helper (no side effects) to assemble configs, use createI18nConfig({ ...overrides }) and feed the result into your own bootstrap logic.

Common overrides:

  • supportedLngs: keep this list scoped to the locales your portal actually serves so language detection stays predictable and bundles stay small.
  • ns / defaultNS: if you share a common namespace across portals, consider setting defaultNS: "common" and trimming the ns array.
  • react: set useSuspense: false for legacy React renderers or provide bindI18n: "languageChanged" when coordinating with data-fetching layers.
  • formatters.fallbackLocale: change this if your organization defaults to a locale other than English; it is used whenever a language code is missing from the locale mapping tables.
  • Module format: The published package currently ships as an ES module build (per tsconfig.json). If your tooling expects CommonJS, configure your bundler to transpile ESM or consider contributing a dual-build setup.

Examples

Use translation + formatting together in a React component:

import React from "react";
import { useI18n } from "@ayinza_tech/i18n-config";

function Price({ amount }: { amount: number }) {
  const { t, formatCurrency } = useI18n();

  return (
    <div>
      <h3>{t("priceHeading")}</h3>
      <p>{formatCurrency(amount)}</p>
    </div>
  );
}

Using formatters directly (non-React):

import { initializeI18n, getFormatters } from "@ayinza_tech/i18n-config";

async function start() {
  await initializeI18n();
  const fmt = getFormatters();
  console.log(fmt.formatCurrency(19.99, "en", "USD"));
}

Using interpolation in translation strings (example en/common.json):

{
  "price": "{{value, currency}}"
}

Then t('price', { value: 19.99, currency: 'EUR' }) will use the registered currency formatter.

Integrating Across Multiple Portals

When sharing this package across portals, keep initialization centralized so each shell bootstraps consistently:

  1. Create a thin wrapper (e.g., packages/i18n/client.ts) that calls initializeI18n with portal-specific overrides such as namespace lists or branding headers.
  2. Import only that wrapper from each portal entry point to keep behavior aligned and avoid forgetting required detectors/backends.
  3. Re-export helpers (useI18n, getFormatters) from your shell layer so downstream micro frontends consume the same singleton instance.
  4. For SSR/Next.js, run initializeI18n inside client components or a useEffect guard to allow detectors to access browser APIs, then hydrate shared hooks.

Example shared bootstrap that portals can reuse:

// packages/i18n/bootstrap.ts
import {
  initializeI18n,
  getFormatters,
  useI18n,
} from "@ayinza_tech/i18n-config";

export async function setupPortalI18n(portalName: string) {
  await initializeI18n({
    config: {
      portalName,
      backend: {
        loadPath: `/locales/${portalName}/{{lng}}/{{ns}}.json`,
      },
      supportedLngs: ["en", "fr", "sw"],
    },
  });

  return {
    i18n: getFormatters(),
    useI18n,
  };
}

// portal-a/src/main.tsx
import { setupPortalI18n } from "@ayinza/portal-shared/i18n";

setupPortalI18n("portal-a").then(() => {
  // mount React app here, all child components can call useI18n()
});

This pattern keeps each portal lightweight while ensuring updates to the core localization stack propagate everywhere by upgrading just this package.

Translation extraction

The package now ships with light wrappers around i18next-parser so every portal can reuse the same extraction defaults and push workflow:

  1. Config helper. Create i18next-parser.config.mjs (or .cjs) that simply exports createI18nextParserConfig({ /* overrides */ }). The helper sets consistent defaults (lexers, separators, indentation, createOldCatalogs, etc.) so every portal parses sources the same way.

    // i18next-parser.config.mjs
    import { createI18nextParserConfig } from "@ayinza_tech/i18n-config";
    
    export default createI18nextParserConfig({
      input: ["src/**/*.{ts,tsx}"],
      locales: ["en"],
      output: "locales/$LOCALE/$NAMESPACE.json",
    });
  2. Detect new keys. Capture a snapshot before and after running the parser (usually for the default locale) by loading your locale JSON and passing it to createTranslationSnapshot, then call collectNewTranslationKeys to compute the delta.

    import { readFile } from "node:fs/promises";
    import path from "node:path";
    import {
      createTranslationSnapshot,
      collectNewTranslationKeys,
      handleNewTranslationKeys,
    } from "@ayinza_tech/i18n-config";
    
    const localesRoot = path.resolve("locales");
    const namespaces = ["translation", "common"];
    
    async function loadNamespaces(locale: string) {
      const entries = await Promise.all(
        namespaces.map(async (namespace) => {
          const filePath = path.join(localesRoot, locale, `${namespace}.json`);
          const raw = await readFile(filePath, "utf8");
          return [namespace, JSON.parse(raw) as Record<string, unknown>];
        })
      );
    
      return Object.fromEntries(entries) as Record<
        string,
        Record<string, unknown>
      >;
    }
    
    const before = createTranslationSnapshot({
      locale: "en",
      namespaces: await loadNamespaces("en"),
    });
    
    // Run `npx i18next --config i18next-parser.config.mjs "src/**/*.{ts,tsx}"`
    
    const after = createTranslationSnapshot({
      locale: "en",
      namespaces: await loadNamespaces("en"),
    });
    
    const newKeys = collectNewTranslationKeys({ previous: before, next: after });
  3. Push or log. Pass the detected keys to handleNewTranslationKeys to run a dry-run or POST them to your translation management service. Configure the helper with portal-specific metadata so CI logs stay readable.

    await handleNewTranslationKeys({
      newKeys,
      pushConfig: {
        portalName: "admin-shell",
        pushUrl: process.env.TRANSLATION_PUSH_URL,
        authorizationToken: process.env.TRANSLATION_PUSH_TOKEN,
        dryRun: process.env.CI === "true" && process.env.DRY_RUN === "true",
      },
    });

If pushUrl is omitted or dryRun is true, the helper only logs detected keys. Provide a custom fetchImpl via ParserPushConfig when running on Node versions older than 18 (which lack global.fetch).

Testing

There are unit tests for I18nFormatters (see src/formatters.test.ts) and for the config merge helper (see src/config.test.ts). Run tests with the provided npm scripts:

npm test
# watch mode during development
npm run test:watch

Note: this repository includes Jest devDependencies configured for TypeScript.

Development

  • Build: npm run build (compiles to dist/ using tsc)
  • Watch: npm run build:watch
  • Test: npm test or npm run test:watch

If you intend to contribute, please run tests and add coverage for new features.

License

MIT — see the LICENSE file in this repository.

Next steps & suggestions

  • Add CI (GitHub Actions) to run tests and build on push/PR.
  • Add usage examples / Storybook snippets for React components that depend on formatting.
  • Consider publishing with changelog and semantic-release for automated releases.
  • Allow consumers to provide a custom logger/debug handler so initialization logs can be routed through their monitoring stack instead of console.