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

bhasha-js

v0.4.0

Published

React i18n built for South Asian languages — Hindi, Bengali, Urdu, Tamil, Telugu, Marathi, Punjabi, Gujarati, Kannada, Malayalam, Nepali, Sinhala — with auto RTL, lakh/crore numbers, native digits, region-aware currency, and CLDR plurals.

Downloads

237

Readme

bhasha-js · भाषाJS

React i18n built for South Asian languages — Hindi, Bengali, Urdu, Tamil, Telugu, Marathi, Punjabi, Gujarati, Kannada, Malayalam, Nepali, Sinhala. RTL switching for Urdu, native script fonts, lakh/crore numbers (₹12,34,567), region-aware currency, and CLDR plurals — all out of the box.

npm version license bundle size

npm install bhasha-js
import { I18nProvider, useTranslation, LanguageSwitcher } from "bhasha-js";

const translations = {
  en: { "hero.title": "Welcome", "items": "{count} items" },
  hi: { "hero.title": "स्वागत है", "items": "{count} आइटम" },
  ur: { "hero.title": "خوش آمدید" },
};

export default function App() {
  return (
    <I18nProvider preloadedTranslations={translations} defaultLang="en">
      <LanguageSwitcher />
      <Page />
    </I18nProvider>
  );
}

function Page() {
  const { t, formatCurrency } = useTranslation();
  return (
    <>
      <h1>{t("hero.title")}</h1>
      <p>{t("items", { count: 5 })}</p>
      <p>{formatCurrency(1234567)}  {/* → ₹12,34,567.00 */}</p>
    </>
  );
}

That's the entire setup. Switch to Urdu and the layout flips RTL automatically. Switch to Bengali and the right Bengali font loads. Pass count: 0 and Hindi correctly treats it as singular while English treats it as plural.


Why bhasha-js

Generic i18n libraries (i18next, react-intl, next-intl) are technically language-agnostic — they support Hindi the same way they support German. That isn't enough for South Asian apps. bhasha-js solves the things they ignore:

| | i18next / react-intl | bhasha-js | |---|---|---| | Auto RTL/LTR switch | manual setup | automatic — sets dir, lang, CSS vars | | Script-aware fonts | your problem | auto-loads Noto Sans Devanagari, Bengali, Tamil, Nastaliq Urdu, etc. | | Lakh/crore grouping | generic Intl | 12,34,567 not 1,234,567 | | Native digits | manual | useNativeDigits → १२,३४,५६७ / ১২,৩৪,৫৬৭ / ௧௨,௩௪,௫௬௭ | | Plural rules | configure each language | built-in CLDR for 14 languages (Hindi 0=singular!) | | Fallback chains | language → English | culturally-aware (Bengali → Hindi → English; Tamil → English direct) | | Currency | your problem | region-aware (₹ for India, ৳ for Bangladesh, Rs for Pakistan, රු for Sri Lanka) | | Bundle size | 10–30 kB | small, tree-shakeable | | Languages supported | depends on your config | 14 South Asian languages pre-configured |


3 ways to use bhasha-js

Pick whichever fits your project. You can switch modes later — the API is the same.

Mode 1 · Bundled translations (no backend, 30 seconds)

The simplest path. Your translations live in your repo as JSON. Best for static sites, marketing pages, or apps with a small fixed string set.

import { I18nProvider } from "bhasha-js";
import en from "./locales/en.json";
import hi from "./locales/hi.json";
import ur from "./locales/ur.json";

<I18nProvider
  preloadedTranslations={{ en, hi, ur }}
  defaultLang="en"
>
  <App />
</I18nProvider>

No API call. No backend. You ship the JSON in your bundle.

Locale JSON can be flat or nested. Nested objects are flattened with dots by the CLI and import tools, so { "checkout": { "title": "Checkout" } } becomes checkout.title.

Mode 2 · Hosted dashboard (recommended for production)

Sign up at bhashajs.com, create a project, get an API key, and translations are managed in a UI (with AI translation, team review, glossary, history).

<I18nProvider
  projectKey="bjs_your_project_api_key_here"
  defaultLang="en"
>
  <App />
</I18nProvider>

The SDK fetches translations from https://api.bhashajs.com/api using the x-api-key header. Bundles are cached in memory and, by default, persisted to localStorage for stale-while-revalidate first paint. Set persistCache={false} to disable storage.

Mode 3 · Self-hosted

The whole stack is open source. docker compose up and you own it. Point the SDK at your URL:

<I18nProvider
  projectKey="bjs_..."
  apiUrl="https://i18n.your-company.com/api"
  defaultLang="en"
>
  <App />
</I18nProvider>

See the main repo for self-hosting docs.


What's new in 0.3

  • Type-safe keys + bhasha CLInpx bhasha pull generates a declaration that makes t() / <Trans> autocomplete your keys and turns a typo into a compile error. Zero config; falls back to string when not generated.
  • Next.js App Router — the main entry is marked "use client", and pure formatters are importable from bhasha-js/server inside Server Components.
  • Framework-agnostic enginebhasha-js/vanilla exposes BhashaStore (subscribe/emit) for Vue, Svelte, vanilla JS, and React Native. See the frameworks guide.
  • Correctness fixes — interpolation no longer crashes/corrupts on regex-special keys or $-values; lakh/crore now correct for PKR & LKR; formatDate returns "" (not "Invalid Date") on bad input; SSR-safe; race-guarded language/register switches.

What's new in 0.2

  • Register-aware translations — same key, three formality variants (default / formal / casual). Casual leans into code-mixing; formal sticks to native vocabulary.
  • Code-mixed locales as first-classhi-Latn (Hinglish), ne-Latn (Roman Nepali), ur-Latn (Roman Urdu), bn-Latn (Banglish), pa-Latn (Roman Punjabi). Real locales with their own translation memory, plural rules, and fallback chains.
  • Segment-aware register switching — pass userSegment + segmentRules and the SDK picks the right register at render time. Same t("hero.cta") returns "Add करो" for Gen-Z and "जोड़ें" for enterprise users.
  • Compliance lock — keys marked regulated (auto-set by regulator-pinned vertical packs) refuse to serve AI drafts. Only human-approved values reach end users.
  • Voice-ready outputs — every (lang, register) cell can produce IPA + SSML. formatPhonetic("hero.cta") and formatSSML("hero.cta") give you everything a TTS engine (AWS Polly, Google Cloud TTS, ElevenLabs) needs.

Translation Memory flywheel

Every approved AI translation becomes a (source, lang, register, target) row in your project's translation memory. Today this improves AI translations via in-prompt examples. Once a (lang, register) cell has ~5,000 verified pairs, that corpus is large enough to fine-tune a small open model (LoRA on a 7B base) for register-aware South-Asian translation that doesn't depend on Gemini at runtime. The dashboard surfaces TM coverage as a visible counter — your moat compounds with every approval.


Features

Auto RTL switching for Urdu

const { setLang } = useTranslation();
setLang("ur");
// → <html lang="ur" dir="rtl">  — entire layout flips
setLang("hi");
// → <html lang="hi" dir="ltr">  — flips back

Smart font loading

Each script needs the right font to render conjuncts correctly. bhasha-js loads them automatically:

| Script | Font | |---|---| | Devanagari (Hindi, Marathi, Nepali) | Noto Sans Devanagari | | Bengali | Noto Sans Bengali | | Urdu | Noto Nastaliq Urdu | | Tamil | Noto Sans Tamil | | Telugu | Noto Sans Telugu | | Gurmukhi (Punjabi) | Noto Sans Gurmukhi | | Gujarati | Noto Sans Gujarati | | Kannada | Noto Sans Kannada | | Malayalam | Noto Sans Malayalam | | Sinhala | Noto Sans Sinhala |

The active font is exposed as the CSS variable --bhasha-font so you can use it in your own styles:

body { font-family: var(--bhasha-font); }

Lakh/crore numbers

import { formatNumber } from "bhasha-js";

formatNumber(1234567, "hi");                           // → "12,34,567"
formatNumber(1234567, "hi", undefined, { useNativeDigits: true });  // → "१२,३४,५६७"
formatNumber(1500000, "hi", undefined, { compact: true });          // → "15 लाख"
formatNumber(20000000, "hi", undefined, { compact: true });         // → "2 करोड़"

Note: en defaults to the en-IN locale — it uses lakh grouping and ₹. Pass region="US" (or the corresponding option to formatNumber) if you want Western formatting for English.

Region-aware currency

import { formatCurrency } from "bhasha-js";

formatCurrency(1500, "hi");          // → "₹1,500.00"  (Hindi defaults to India)
formatCurrency(1500, "bn");          // → "৳1,500.00"  (Bengali defaults to Bangladesh)
formatCurrency(1500, "bn", "IN");    // → "₹1,500.00"  (Bengali in India)
formatCurrency(1500, "ta", "LK");    // → "Rs. 1,500.00"  (Tamil in Sri Lanka)
formatCurrency(1500, "ur");          // → "Rs 1,500.00"  (Urdu defaults to Pakistan)

Currencies are auto-detected from language + region. Override with the currency option ({ currency: "USD" }).

CLDR pluralization (Hindi 0 = singular!)

This is one of the most-broken things in other libraries. In Hindi (and most Indo-Aryan languages), 0 is treated as singular, not plural.

// In your translations file:
{
  "items_count_one": "{count} आइटम",
  "items_count_other": "{count} आइटम"
}

// In your component:
t("items_count", { count: 0 })   // Hindi → "0 आइटम"   (uses _one)
t("items_count", { count: 0 })   // English → "0 items"  (uses _other)

The SDK reads count, picks the correct CLDR category for the active language, and looks up the suffixed key (_one or _other).

Culturally-aware fallback chains

If a translation is missing in the active language, the SDK walks a chain that makes linguistic sense:

| Language | Fallback | |---|---| | Hindi (hi) | hi → en | | Bengali (bn) | bn → hi → en | | Urdu (ur) | ur → hi → en | | Marathi (mr) | mr → hi → en | | Gujarati (gu) | gu → hi → en | | Punjabi-Gurmukhi (pa) | pa → hi → en | | Punjabi-Shahmukhi (pa-PK) | pa-PK → ur → en | | Nepali (ne) | ne → hi → en | | Tamil (ta) | ta → en (no Hindi — different language family) | | Telugu (te) | te → en (Dravidian) | | Kannada (kn) | kn → en (Dravidian) | | Malayalam (ml) | ml → en (Dravidian) | | Sinhala (si) | si → en |

Dravidian languages skip Hindi because they're from a completely different language family — falling back to a related language doesn't apply.

String interpolation

t("greeting", { name: "Rohan", count: 5 })
// "Hello {name}, you have {count} items" → "Hello Rohan, you have 5 items"

Date formatting (DD/MM/YYYY)

import { formatDate } from "bhasha-js";

formatDate(new Date(), "hi", undefined, { preset: "short" });  // "25/4/26"
formatDate(new Date(), "hi", undefined, { preset: "medium" }); // "25 अप्रैल 2026"
formatDate(new Date(), "hi", undefined, { preset: "long" });   // "25 अप्रैल 2026"
formatDate(new Date(), "hi", undefined, { preset: "full" });   // "शनिवार, 25 अप्रैल 2026"
formatDate(new Date(), "hi", undefined, { preset: "long", useNativeDigits: true });
// → "२५ अप्रैल २०२६"

CLI

npx bhasha init
npx bhasha pull
npx bhasha push --lang hi --register formal
npx bhasha scan --strict

bhasha push reads flat or nested JSON files from locales/ and sends them to the dashboard. Use a scoped API key with read-only OFF and no origin allowlist. Regulated keys are reported as skipped because they can only be edited by a human in the dashboard.

API reference

<I18nProvider>

| Prop | Type | Default | Description | |---|---|---|---| | projectKey | string | — | Project API key (Mode 2/3). Recommended for production. | | projectId + apiToken | string + string | — | JWT auth (advanced). | | preloadedTranslations | Record<string, Record<string, string>> | — | Mode 1 — bundle translations into your app. | | persistCache | boolean | true | Persist fetched text bundles to localStorage and refresh them in the background. | | defaultLang | string | "en" | Initial language code. | | apiUrl | string | "https://api.bhashajs.com/api" | Override for self-hosting (e.g. "https://my.host/api"). | | region | string | — | Override default region (e.g. "IN", "BD", "PK", "LK", "NP"). Affects currency + locale. | | onLanguageChange | (lang: string) => void | — | Callback fired whenever the language changes. |

useTranslation()

const {
  t,                  // (key, params?) => string
  currentLang,        // "hi"
  setLang,            // (lang: string) => void
  supportedLangs,     // string[]
  isLoading,          // boolean
  error,              // string | null  ← surfaces auth/network errors
  formatNumber,       // (value, options?) => string
  formatCurrency,     // (value, options?) => string
  formatDate,         // (date, options?) => string
} = useTranslation();

useLangInfo()

const { code, name, englishName, dir, font, script, defaultRegion, intlLocale, defaultCurrency } = useLangInfo();

<LanguageSwitcher>

| Prop | Type | Default | |---|---|---| | style | "dropdown" \| "floating" | "dropdown" | | position | "top-right" \| "top-left" \| "bottom-right" \| "bottom-left" | "top-right" | | className | string | — |

<Trans>

<Trans id="hero.title" />
<Trans id="greeting" params={{ name: "Rohan" }} as="h1" />

Standalone utilities (no React required)

import {
  formatNumber, formatCurrency, formatDate,
  getPluralCategory, getFallbackChain, getLangInfo,
  LANGUAGES, REGION_OVERRIDES,
} from "bhasha-js";

Supported languages (14 total · ~1.8 billion speakers)

| Code | Language | Native | Script | Direction | Default region | Default currency | |---|---|---|---|---|---|---| | en | English | English | Latin | LTR | IN | INR | | hi | Hindi | हिन्दी | Devanagari | LTR | IN | INR | | bn | Bengali | বাংলা | Bengali | LTR | BD | BDT | | ur | Urdu | اردو | Nastaliq | RTL | PK | PKR | | ta | Tamil | தமிழ் | Tamil | LTR | IN | INR | | te | Telugu | తెలుగు | Telugu | LTR | IN | INR | | mr | Marathi | मराठी | Devanagari | LTR | IN | INR | | ne | Nepali | नेपाली | Devanagari | LTR | NP | NPR | | pa | Punjabi (Gurmukhi) | ਪੰਜਾਬੀ | Gurmukhi | LTR | IN | INR | | pa-PK | Punjabi (Shahmukhi) | پنجابی | Arabic-derived | RTL | PK | PKR | | gu | Gujarati | ગુજરાતી | Gujarati | LTR | IN | INR | | kn | Kannada | ಕನ್ನಡ | Kannada | LTR | IN | INR | | ml | Malayalam | മലയാളം | Malayalam | LTR | IN | INR | | si | Sinhala | සිංහල | Sinhala | LTR | LK | LKR |


FAQ

Does this work with Next.js (App Router)? Yes. The main bhasha-js entry is already marked "use client", so you can use the provider and hooks in any Client Component. app/layout.tsx is a Server Component by default, so put the provider in a small client wrapper:

// app/providers.tsx
"use client";
import { I18nProvider } from "bhasha-js";

export function Providers({ children }: { children: React.ReactNode }) {
  return <I18nProvider projectKey="bjs_..." defaultLang="en">{children}</I18nProvider>;
}
// app/layout.tsx  (stays a Server Component)
import { Providers } from "./providers";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body><Providers>{children}</Providers></body>
    </html>
  );
}

Need the formatters inside a Server Component (no provider, no hooks)? Import them from the server-safe entry — it carries no "use client" directive:

import { formatCurrency } from "bhasha-js/server"; // ✅ works in a Server Component
formatCurrency(1234567, "hi"); // "₹12,34,567.00"

Does this work without React? Yes — the pure utilities (formatNumber, formatCurrency, formatDate, getPluralCategory, getLangInfo, …) are framework-agnostic. Import them from bhasha-js/server in any non-React / server context. The components and hooks need React 17+.

How do I integrate AI translation? Use Mode 2 (hosted dashboard) — there's a "Translate with AI" button per language. Or use the /api/translations/:projectId/ai-translate endpoint if self-hosted.

Bundle size? Tree-shakeable ESM. If you only use formatCurrency and formatNumber, that's what ships. Components and hooks are imported separately.

Does it work with React Native / Vue / Svelte? Yes — import the framework-agnostic engine from bhasha-js/vanilla. BhashaStore exposes t(), the formatters, and subscribe() with no React and no hard document dependency (DOM updates are opt-in and SSR-safe). Wrap it in a Vue composable, a Svelte store, or a React Native context — see the Vue, Svelte & vanilla guide.

Are the translation keys type-safe? Run npx bhasha pull and they are — t() and <Trans> autocomplete your keys and a typo becomes a compile error. With no generated file, keys stay typed as string, so it's fully opt-in.


Links

  • 🌐 Website + dashboard: https://bhashajs.com
  • 📚 Docs: https://bhashajs.com/docs
  • 🔧 Source: https://github.com/santoshpant23/bhashajs
  • 🐛 Issues: https://github.com/santoshpant23/bhashajs/issues

License

MIT © Santosh Pant