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

@voilabs/oilang

v0.0.36

Published

A robust internationalization (i18n) handling library designed for performance and flexibility.

Readme

OILang

A robust internationalization (i18n) library for Node.js & Bun — built for performance, flexibility and DX.

npm TypeScript License


OILang is a dual-layer i18n engine: a persistent adapter (PostgreSQL / Drizzle ORM) acts as the source of truth, while a runtime store (Memory / Redis) provides fast access during request handling.

It ships with a first-class CLI (bunx oilang generate), built-in framework handlers (Elysia), and a fully typed API.


Table of Contents


Features

  • Dual-layer architecture (persistent DB + runtime cache)
  • PostgreSQL and Drizzle ORM adapters (pg, mysql, sqlite)
  • CLI schema generator (SQL or Drizzle schema files)
  • First-class TypeScript types
  • Pluggable stores: MemoryStore, RedisStore
  • Built-in Elysia handler with ready-to-use REST endpoints

Installation

bun add @voilabs/oilang

Or with npm:

npm install @voilabs/oilang

Quick Start

PostgreSQL + MemoryStore

import { OILang } from "@voilabs/oilang";
import { PostgreSQL } from "@voilabs/oilang/adapters";
import { MemoryStore } from "@voilabs/oilang/stores";

export const oilang = new OILang({
    database: new PostgreSQL(
        "postgresql://user:password@localhost:5432/dbname",
        {
            schemaNames: {
                keys: "i18n_keys",
                locales: "i18n_locales",
            },
        },
    ),
    store: new MemoryStore(),
});

await oilang.init();

Drizzle + RedisStore

import { OILang } from "@voilabs/oilang";
import { DrizzleAdapter } from "@voilabs/oilang/adapters";
import { RedisStore } from "@voilabs/oilang/stores";
import { db } from "./db";

export const oilang = new OILang({
    database: new DrizzleAdapter(db, {
        provider: "pg", // "pg" | "mysql" | "sqlite"
        schemaNames: {
            keys: "i18n_keys",
            locales: "i18n_locales",
        },
    }),
    store: new RedisStore(process.env.REDIS_URL!),
});

await oilang.init();

CLI

OILang ships with a CLI that statically analyzes your config and generates schema files.

Command

bunx oilang generate --path <output-file-or-directory> --config <path-to-oilang-config>

Examples

Generate a Drizzle schema file:

bunx oilang generate \
  --path ./src/server/drizzle/oilang-schema.ts \
  --config ./src/server/lib/oilang.ts

Generate into a directory (auto-picks file name):

bunx oilang generate --path ./database

If --config is omitted, CLI will:

  1. Read from oilang.config.ts if available.
  2. Otherwise search the project for a file exporting oilang and create a config for you.

Configuration File

OILang uses an optional oilang.config.ts at project root:

import { defineConfig } from "@voilabs/oilang";

export default defineConfig({
    configPath: "./src/server/lib/oilang.ts",
    outputPath: "./src/server/drizzle/oilang-schema.ts",
});

Supported keys:

| Key | Description | | ------------ | ----------------------------------------------- | | configPath | Path to the file exporting your OILang instance | | outputPath | Destination for generated schema file | | output | Alias for outputPath | | path | Alias for outputPath |


API Reference

OILang

Main entry point. Orchestrates the adapter and the store.

| Method | Description | | ---------------- | ----------------------------------------------- | | init() | Connect the adapter and hydrate the store cache | | refreshCache() | Reload all locales/translations into the store | | getAdapter() | Return the configured database adapter instance | | locales | Locale management namespace | | translations | Translation management namespace |

oilang.locales

| Method | Description | | ----------------------------------------------------------------------------------- | ------------------ | | list() | List all locales | | create({ locale, nativeName, englishName, isDefault?, translationsFromDefault? }) | Create a locale | | update(locale, nativeName, englishName, isDefault?) | Update locale info | | delete(locale) | Delete a locale |

oilang.translations

| Method | Description | | ------------------------------------ | ------------------------------------------------- | | list(locale) | List all translations for a locale | | create(locale, { key, value }) | Add a translation | | update(locale, key, newValue) | Update a translation | | delete(locale, key) | Delete a translation | | translate(locale, key, variables?) | Resolve a translation (with variables + fallback) |


Adapters

PostgreSQL

Uses the pg library and manages tables on connect() via CREATE TABLE IF NOT EXISTS.

new PostgreSQL(connectionString, {
    schemaNames: { keys: "i18n_keys", locales: "i18n_locales" },
});

DrizzleAdapter

Works with the Drizzle ORM. Supports PostgreSQL, MySQL and SQLite providers.

new DrizzleAdapter(db, {
    provider: "pg", // "pg" | "mysql" | "sqlite"
    schemaNames: { keys: "i18n_keys", locales: "i18n_locales" },
    schemas: { locales, translations }, // optional; auto-detected otherwise
});
  • schemas is optional. When omitted, OILang tries to discover tables via Drizzle internals.
  • CLI generates a matching Drizzle schema file so you can import it back into your project.

Stores

MemoryStore

Fast in-process cache — ideal for single-node apps and development.

new MemoryStore();

RedisStore

Shared cache for distributed deployments.

new RedisStore(connectionString, { prefix?: "oilang:" });

Elysia Handler

Mount OILang as a ready-to-use Elysia module:

import { Elysia } from "elysia";
import { elysiaHandler } from "@voilabs/oilang/handlers";

new Elysia()
    .use(
        elysiaHandler(oilang, {
            onAuthHandle: async ({ request }) => {
                // optional auth pre-handler
            },
        }),
    )
    .listen(3000);

API Endpoints

All endpoints are mounted under the /oilang prefix.
Endpoints marked 🔒 support the optional onAuthHandle hook.

Locales

| Method | Route | Description | | -------- | ----------------------------- | ---------------------- | | GET | /oilang/locales | List all locales | | POST | /oilang/locales | Create a new locale 🔒 | | PUT | /oilang/locales/:localeCode | Update a locale 🔒 | | DELETE | /oilang/locales/:locale | Delete a locale 🔒 |

POST /oilang/locales body:

{
    "locale": "en-US",
    "native_name": "English",
    "english_name": "English",
    "is_default": true,
    "translations_from_default": false
}

PUT /oilang/locales/:localeCode body:

{
    "native_name": "English",
    "english_name": "English"
}

Translations

| Method | Route | Description | | -------- | ------------------------------ | ------------------------------------------------------ | | GET | /oilang/translations/:locale | Fetch translations. ?format=true returns nested JSON | | POST | /oilang/translations/:locale | Add multiple translations 🔒 | | PUT | /oilang/translations/:locale | Update multiple translations 🔒 | | DELETE | /oilang/translations/:locale | Delete multiple translations 🔒 |

POST / PUT body:

{
    "translations": [
        { "key": "home.title", "value": "Welcome" },
        { "key": "home.subtitle", "value": "Hello there" }
    ]
}

DELETE body:

{
    "translations": [{ "key": "home.title" }]
}

Session

| Method | Route | Description | | ------ | -------------------- | ------------------------------------ | | POST | /oilang/set-locale | Set the active locale on the session |

Body:

{ "locale": "en-US" }

System

| Method | Route | Description | | ------ | ----------------- | ------------------------------------- | | POST | /oilang/refresh | Refresh the internal cache from DB 🔒 |


Utils

wrap(locales: Record<string, string>): string

import { wrap } from "@voilabs/oilang/utils";

wrap({ "en-US": "Hello", "tr-TR": "Merhaba" });
// => "<en-US>Hello</en-US><tr-TR>Merhaba</tr-TR>"

unwrap(string, locale, fallbackLocale?): string

import { unwrap } from "@voilabs/oilang/utils";

unwrap("<en-US>Hello</en-US><tr-TR>Merhaba</tr-TR>", "tr-TR");
// => "Merhaba"

unwrapObject(string): Record<string, string>

import { unwrapObject } from "@voilabs/oilang/utils";

unwrapObject("<en-US>Hello</en-US><tr-TR>Merhaba</tr-TR>");
// => { "en-US": "Hello", "tr-TR": "Merhaba" }

Troubleshooting

  • If the CLI cannot resolve your config, check configPath inside oilang.config.ts.
  • When CLI runtime import fails, it falls back to static source analysis automatically.
  • Missing schemaNames? Use explicit string literals in your config so the analyzer can detect them.
  • On Windows, CLI internally uses proper file URLs for path compatibility.

Changelog

[0.0.36] - 2026-04-23

Fixed

  • Elysia translations ?format=true (src/handlers/Elysia.ts):
    • Dotted keys with numeric path segments (e.g. abc.0.t, abc.1.t) are now built as real arrays, e.g. { abc: [{ t: "…" }, { t: "…" }] }, instead of objects with string keys "0" / "1".

[0.0.35] - 2026-04-22

Changed

  • CLI Internal Refactor (src/cli):
    • Database schema generation logic was split into dedicated modules under src/cli/utils/databases/.
    • generate command now uses modular static-analysis helpers (drizzle, postgres, shared, index).
  • Adapter Contract Simplification:
    • Removed getFileInfo() requirement from the adapter interface and implementations.
    • Schema generation is now fully handled by CLI static analysis.

Fixed

  • PostgreSQL Runtime Bootstrap (src/adapters/PostgreSQL.ts):
    • Restored CREATE TABLE IF NOT EXISTS flow inside connect() so PostgreSQL users keep auto-init behavior.

[0.0.34] - 2026-04-22

Fixed

  • Improved schemaNames parsing for more robust keys/locales detection.
  • PostgreSQL fallback generation now better respects custom schemaNames.

[0.0.33] - 2026-04-22

Fixed

  • Resilient config loading for CLI: importrequire → static source-analysis fallback.
  • Better schema generation reliability for PostgreSQL and drizzleAdapter configs.

[0.0.32] - 2026-04-22

Changed

  • Safer config loading flow (ESM/CJS compatibility).
  • Better oilang.config.ts handling (configPath, outputPath, output, path).
  • CLI refactored into modular folders (commands, utils, index).

[0.0.31] - 2026-04-22

Changed

  • Added defineConfig(...) helper and extended config keys.

[0.0.30] - 2026-04-22

Fixed

  • Windows config import path compatibility using proper file URL conversion.

[0.0.29] - 2026-04-22

Added

  • Drizzle adapter: optional schemas + automatic schema discovery.

[0.0.28] - 2026-04-22

Changed

  • Standardized adapter file generation via { content, name }.
  • Improved output target resolution.

[0.0.27] - 2026-04-22

Added

  • Drizzle ORM support (src/adapters/Drizzle.ts).
  • CLI generator (bunx oilang generate).

[0.0.25] - 2026-02-17

Added

  • Initial public release (OILang, locale/translation APIs, stores, utilities).