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

bilang

v1.0.3

Published

A lightweight, framework-agnostic multi-language localization and translation utility designed for web apps and SSR setups.

Readme

bilang

Bi/Multi + Language => Bi/Mul + lang => Bilang

A lightweight, framework-agnostic multi-language localization and translation utility designed for modern web applications and Server-Side Rendering (SSR) setups.

It provides a highly optimized way to implement bilingual or multilingual features, manage local/server dictionaries, dynamically translate missing phrases using remote translators, and handle language-specific number/unit conversions.


Features

  • 🌐 Framework-Agnostic: Core logic is pure JS/TS. Runs perfectly in Vanilla JS, Node.js, React, Vue, Svelte, Next.js, and Nuxt.
  • ⚡ SSR & Hydration Safe: Built-in payloads support to completely eliminate hydration mismatches (specifically tailored for Nuxt and Next setups).
  • 🔄 Async Auto-Translation Fallback: Dynamic client-side translation of missing phrases. Automatically falls back to the free built-in MyMemory API, or lets you easily supply your own translation service (e.g., Google Translate).
  • 🔢 Custom Replacement Rules: Built-in or custom locale-based replacements for converting digits, units, times, and dates automatically.
  • 🏷️ Dynamic Languages Enum: Exposes a LANGUAGES enum helper dynamically generated from configured language keys (e.g. { EN: 'ENG', BN: 'বাংলা' }).

Installation

npm install bilang

Core Concepts & Methods

bilang centers around three simple actions to manage translations in your components:

1. Explicit Bilingual Translation (.bi())

Use .bi() when you already have both translations and want to write them directly inline.

  • Syntax: bi(sourceText, targetTranslation)
  • Behavior: Returns the first string if the active language is the source language (e.g., English), and the second string if the target language is active.
translator.bi("Welcome", "স্বাগতম");
// ➡️ Returns "Welcome" if language is 'ENG'
// ➡️ Returns "স্বাগতম" if language is 'বাংলা'

2. Dictionary / Auto-Translate Lookup (.get())

Use .get() when you only pass the source string, and want to look up the translation dynamically from a JSON dictionary file or request it via your translation API.

  • Syntax: get(sourceText)
  • Behavior: Instantly returns the translation from the dictionary cache. If it doesn't exist, it returns the source text immediately (to prevent SSR hydration mismatches) and calls the remote translator in the background client-side.
translator.get("Brand name");
// ➡️ Returns "ব্র্যান্ডের নাম" (from dictionary)

3. Setting Languages (.set())

Use .set() to switch languages programmatically.

  • Syntax: set(langValue)
  • Behavior: Changes the active language state, saves to storage, updates URL prefixes, and triggers routing transitions.
translator.set('বাংলা');

Core Initialization API

Initialize bilang with a configuration object:

import { Bilang, BANGLA_REPLACEMENTS } from 'bilang';

const translator = new Bilang({
  // Initial active language representation
  defaultLang: 'বাংলা', 

  // Mapping of route prefix path matching to active language
  languages: {
    en: 'ENG',
    bn: 'বাংলা'
  },

  // Optional custom languages enum (if omitted, generated as { EN: 'ENG', BN: 'বাংলা' })
  LANGUAGES: {
    ENGLISH: 'ENG',
    BANGLA: 'বাংলা'
  },

  // Source language (no translations needed for this; defaults to first language value)
  sourceLanguage: 'ENG',

  // Active dictionary memory cache
  dictionary: {}, 

  // Optional per-language custom replacements
  replacements: {
    bn: BANGLA_REPLACEMENTS // Curated Bangla digit/unit replacements
  },

  // Pluggable Storage Adapter (Defaults to localStorage client-side and memory/config server-side)
  storage: {
    getItem(key) { ... },
    setItem(key, value) { ... }
  },

  // Optional: Custom dynamic translator service callback (defaults to using free MyMemory API)
  // Set to `false` to disable dynamic translation entirely (offline dictionary-only mode)
  translateService: undefined, // Or omit entirely to use the default built-in translator

  // Redirection prefix change callback
  urlPrefixRedirection: true, // Auto prefix window.location.href in browser
  onLanguageChange: (langPrefix, newFullPath) => {
     // Run custom client-side router transition instead of browser reload
     router.push(newFullPath);
  }
});

Auto-Translation Fallback (Dynamic Translator)

By default, bilang has a built-in translator using the free public MyMemory API that handles on-the-fly client-side translations out of the box. You don't have to define a custom translation service callback.

1. Zero-Configuration Default Translator (Default)

Simply omit the translateService option (or pass undefined), and bilang will automatically translate any missing dictionary keys to the active target language:

const translator = new Bilang({
  defaultLang: 'বাংলা',
  languages: {
    en: 'ENG',
    bn: 'বাংলা'
  }
  // translateService is automatically set to use the built-in MyMemory API translator
});

2. Disable Auto-Translation

If you want to run strictly in offline dictionary-only mode (returning the source text immediately if a translation is missing), set translateService: false:

const translator = new Bilang({
  languages: { en: 'ENG', bn: 'বাংলা' },
  translateService: false // Disable remote translation fallback entirely
});

3. Custom Translation Service

If you want to use your own translation endpoint, Google Cloud Translate API, DeepL, or any custom API:

const translator = new Bilang({
  languages: { en: 'ENG', bn: 'বাংলা' },
  translateService: async (text, targetLang) => {
    // text: lowercased text to translate
    // targetLang: target language value, e.g. 'বাংলা'
    const res = await fetch(`/api/translate?text=${encodeURIComponent(text)}&target=${targetLang}`);
    const data = await res.json();
    return data.translatedText;
  }
});

Languages Enum (LANGUAGES)

The Bilang instance exposes a LANGUAGES property that lists all supported languages as key-value pairs. This helps you avoid hardcoding language values (like 'বাংলা') and prevents typos in your component files.

1. Auto-Generated Enum

By default, the enum keys are generated by converting your languages prefix keys into uppercase:

const translator = new Bilang({
  languages: {
    en: 'ENG',
    bn: 'বাংলা'
  }
});

console.log(translator.LANGUAGES);
// Output: { EN: 'ENG', BN: 'বাংলা' }

2. Custom Explicit Enum

If you prefer descriptive names (e.g., ENGLISH, BANGLA), you can provide a custom LANGUAGES map in the configuration options:

const translator = new Bilang({
  languages: { en: 'ENG', bn: 'বাংলা' },
  LANGUAGES: {
    ENGLISH: 'ENG',
    BANGLA: 'বাংলা'
  }
});

console.log(translator.LANGUAGES);
// Output: { ENGLISH: 'ENG', BANGLA: 'বাংলা' }

3. Usage Example (Switching Languages Safely)

In your component layouts or controllers:

// Toggle language safely using the enum
const nextLang = translator.lang === translator.LANGUAGES.EN
  ? translator.LANGUAGES.BN
  : translator.LANGUAGES.EN;

translator.set(nextLang);

Multi-Language Support (3+ Languages)

bilang is fully scalable and designed to handle three or more languages dynamically. Below is an example configuration for a 4-language setup (English, Bangla, Spanish, and Arabic):

1. Dynamic Configuration

You can register any number of key-value pairs in the languages mapping:

const translator = new Bilang({
  defaultLang: 'বাংলা',
  languages: {
    en: 'ENG',
    bn: 'বাংলা',
    es: 'ESPAÑOL',
    ar: 'العربية'
  },
  // Optionally specify language-specific replacements
  replacements: {
    bn: BANGLA_REPLACEMENTS,
    ar: ARABIC_REPLACEMENTS
  }
});

2. Loading Language-Specific Dictionaries

In multi-language applications, load only the active translation dictionary bundle to optimize performance:

import bnDict from './locales/bn.json';
import esDict from './locales/es.json';
import arDict from './locales/ar.json';

// Detect active language from URL prefix (e.g. /es/...)
const urlPrefix = window.location.pathname.split('/')[1] || 'en';

// Resolve appropriate dictionary object
let activeDictionary = {};
if (urlPrefix === 'bn') activeDictionary = bnDict;
else if (urlPrefix === 'es') activeDictionary = esDict;
else if (urlPrefix === 'ar') activeDictionary = arDict;

const translator = new Bilang({
  languages: { en: 'ENG', bn: 'বাংলা', es: 'ESPAÑOL', ar: 'العربية' },
  dictionary: activeDictionary
});

3. Dynamic Translator Routing

The translateService receives the current target language string as its second parameter (targetLang). You can map this value back to your translation API language code request:

translateService: async (text, targetLang) => {
  // Map internal name to API language code
  const targetCode = targetLang === 'ESPAÑOL' ? 'es' : (targetLang === 'العربية' ? 'ar' : 'bn');

  const response = await fetch(`/api/translate?text=${encodeURIComponent(text)}&target=${targetCode}`);
  const data = await response.json();
  return data.translatedText;
}

Integration Guides

1. Vanilla JavaScript Setup

import { Bilang, BANGLA_REPLACEMENTS } from 'bilang';

const bilang = new Bilang({
  languages: { en: 'ENG', bn: 'বাংলা' },
  replacements: { bn: BANGLA_REPLACEMENTS }
});

// Setup active language prefix based on URL path segment
const prefix = window.location.pathname.split('/')[1] || 'en';
bilang.set(bilang.detectUrlLang(prefix));

// 1. Using .bi() (explicit bilingual translation)
document.getElementById('header-txt').innerText = bilang.bi('Hello', 'হ্যালো');

// 2. Using .get() (dictionary lookup with dynamic fallback)
document.getElementById('welcome-txt').innerText = bilang.get('Welcome to our website');

// 3. Using .set() (change active language and trigger redirect)
document.getElementById('lang-toggle-btn').addEventListener('click', () => {
  const nextLang = bilang.lang === bilang.LANGUAGES.EN ? bilang.LANGUAGES.BN : bilang.LANGUAGES.EN;
  bilang.set(nextLang);
});

2. Nuxt 3 (SSR Safe Integration)

Plugin setup to keep the translation state synced from server-to-client and avoid hydration warnings:

// plugins/bilang.js
import { defineNuxtPlugin, useRoute } from '#imports';
import { Bilang, BANGLA_REPLACEMENTS } from 'bilang';
import Dictionary from '~/services/Language/dictionary.json';

export default defineNuxtPlugin((nuxtApp) => {
    const { req } = nuxtApp.ssrContext || {}; 

    // Determine current language from route param or request URL path
    let urlLang = 'en';
    try {
        const route = useRoute();
        urlLang = route.params.langUri || 'en';
    } catch (e) {
        if (process.server) {
            urlLang = req?.url?.split('/')[1] || 'en';
        } else if (process.client) {
            urlLang = window.location.pathname.split('/')[1] || 'en';
        }
    }

    const bilang = new Bilang({
        defaultLang: 'বাংলা',
        languages: {
            en: 'ENG',
            bn: 'বাংলা'
        },
        dictionary: Dictionary,
        replacements: {
            bn: BANGLA_REPLACEMENTS
        }
    });

    // Set active language prefix
    bilang.set(bilang.detectUrlLang(urlLang));

    // Dynamic state serialization prevents client-side hydration mismatches
    if (process.server) {
        nuxtApp.payload.bilangDictionary = bilang.source;
    } else if (process.client && nuxtApp.payload.bilangDictionary) {
        Object.assign(bilang.source, nuxtApp.payload.bilangDictionary);
    }

    nuxtApp.provide('bilang', bilang);
});

Usage in Nuxt page template:

<script setup>
const { $bilang } = useNuxtApp();
</script>

<template>
  <div>
    <!-- 1. Using .bi() -->
    <h1>{{ $bilang.bi('Home', 'হোম') }}</h1>

    <!-- 2. Using .get() -->
    <p>{{ $bilang.get('Popular Cars') }}</p>

    <!-- 3. Using .set() -->
    <button @click="$bilang.set($bilang.lang === $bilang.LANGUAGES.EN ? $bilang.LANGUAGES.BN : $bilang.LANGUAGES.EN)">
      Switch Language
    </button>
  </div>
</template>

3. Vue 3 Setup

You can provide bilang globally to be injected in any component, or define global properties for template access:

// main.js
import { createApp } from 'vue';
import { Bilang, BANGLA_REPLACEMENTS } from 'bilang';
import App from './App.vue';
import Dictionary from './dictionary.json';

const app = createApp(App);

const bilang = new Bilang({
  defaultLang: 'বাংলা',
  languages: { en: 'ENG', bn: 'বাংলা' },
  dictionary: Dictionary,
  replacements: { bn: BANGLA_REPLACEMENTS }
});

// Provide globally for setup components
app.provide('$bilang', bilang);

// Global properties for template shortcut usage
app.config.globalProperties.$bi = (en, bn) => bilang.bi(en, bn);
app.config.globalProperties.$get = (phrase) => bilang.get(phrase);

app.mount('#app');

Usage in Vue 3 Component:

<script setup>
import { inject } from 'vue';
const bilang = inject('$bilang');
</script>

<template>
  <div>
    <!-- 1. Using .bi() -->
    <h1>{{ bilang.bi('Welcome', 'স্বাগতম') }}</h1>
    
    <!-- 2. Using .get() -->
    <p>{{ bilang.get('Search products') }}</p>
    <!-- Or using global properties -->
    <p>{{ $get('Brand Name') }}</p>

    <!-- 3. Using .set() -->
    <button @click="bilang.set(bilang.lang === bilang.LANGUAGES.EN ? bilang.LANGUAGES.BN : bilang.LANGUAGES.EN)">
      Toggle Language
    </button>
  </div>
</template>

4. React Setup

Wrap the app in a custom provider using React Context to broadcast state updates correctly:

// BilangContext.js
import React, { createContext, useContext, useState } from 'react';
import { Bilang, BANGLA_REPLACEMENTS } from 'bilang';
import Dictionary from './dictionary.json';

const bilangInstance = new Bilang({
  defaultLang: 'বাংলা',
  languages: { en: 'ENG', bn: 'বাংলা' },
  dictionary: Dictionary,
  replacements: { bn: BANGLA_REPLACEMENTS }
});

const BilangContext = createContext(null);

export const BilangProvider = ({ children }) => {
  const [lang, setLangState] = useState(bilangInstance.lang);

  const changeLanguage = (newLang) => {
    bilangInstance.set(newLang);
    setLangState(newLang); // Triggers React re-render
  };

  const value = {
    lang,
    setLang: changeLanguage,
    bi: (en, bn) => bilangInstance.bi(en, bn),
    get: (phrase) => bilangInstance.get(phrase),
    LANGUAGES: bilangInstance.LANGUAGES
  };

  return (
    <BilangContext.Provider value={value}>
      {children}
    </BilangContext.Provider>
  );
};

export const useBilang = () => useContext(BilangContext);

Usage in React Component:

import React from 'react';
import { useBilang } from './BilangContext';

const ProductComponent = () => {
  const { bi, get, lang, setLang, LANGUAGES } = useBilang();

  return (
    <div>
      {/* 1. Using .bi() */}
      <h2>{bi('Product Details', 'পণ্যের বিবরণ')}</h2>

      {/* 2. Using .get() */}
      <p>{get('Compare Specs')}</p>

      {/* 3. Using .set() */}
      <button onClick={() => setLang(lang === LANGUAGES.EN ? LANGUAGES.BN : LANGUAGES.EN)}>
        Switch to {lang === LANGUAGES.EN ? 'বাংলা' : 'English'}
      </button>
    </div>
  );
};

License

MIT License. Feel free to use and contribute!