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

@regraf/i18n

v1.0.9

Published

Internationalization module for Regraf

Readme

@regraf/i18n

Internationalization (i18n) module for Regraf - a modern fork of telegraf Telegram bot framework.

Features

  • 🌍 Multi-language support with fallback mechanism
  • 📝 String interpolation with placeholders
  • 🔢 Pluralization rules for different count values
  • 💾 Memory caching with TTL support
  • 🗂️ Automatic language file preloading
  • 🎯 TypeScript support with full type definitions
  • 🔧 Flexible language resolution strategies

Installation

npm install @regraf/i18n
# or
yarn add @regraf/i18n
# or
pnpm add @regraf/i18n

Quick Start

import { Regraf } from 'regraf';
import { LanguageController } from '@regraf/i18n';

const bot = new Regraf(process.env.BOT_TOKEN!);

// Create language controller
const i18n = new LanguageController({
  fallbackLanguage: 'en',
  // Optional: custom language resolver
  languageResolver: (ctx) => ctx.from?.language_code ?? 'en'
});

// Preload language files from directory
i18n.preloadLanguages('./locales');

// Use i18n middleware
bot.use(i18n.middleware);

// Use translations in your handlers
bot.command('start', (ctx) => {
  ctx.reply(ctx.lang.t('welcome', { name: ctx.from.first_name }));
});

bot.launch();

Language Files Structure

Create JSON files for each language in your locales directory:

locales/en.json

{
  "welcome": "Welcome, {{name}}!",
  "greeting": "Hello!",
  "item.zero": "No items",
  "item.one": "One item",
  "item.few": "{{count}} items",
  "item.many": "{{count}} items"
}

locales/ru.json

{
  "welcome": "Добро пожаловать, {{name}}!",
  "greeting": "Привет!",
  "item.zero": "Нет элементов",
  "item.one": "{{count}} элемент",
  "item.few": "{{count}} элемента",
  "item.many": "{{count}} элементов"
}

API Reference

LanguageController

The main controller class for managing translations.

Constructor Options

interface LanguageControllerOptions {
  loader?: Loader<string, Record<string, string>, void>;
  ttl?: number;
  languageResolver?: (ctx: RegrafContext) => string | string[];
  fallbackLanguage?: string;
}
  • loader - Custom loader function for language data
  • ttl - Time-to-live for cache entries in seconds (default: never expires)
  • languageResolver - Function to determine user's language from context
  • fallbackLanguage - Default language when user's language is not available (default: "en")

Methods

preloadLanguages(folderOrFile: string): void

Preloads language files from a directory or specific file path. Recursively processes directories and loads all JSON files.

middleware(ctx: LanguageContext, next: () => Promise<void>): Promise<void>

Middleware function that adds language support to the context. Adds lang property to the context.

Language

Class representing a specific language with translation data.

Methods

t(path: string, placeholders?: Record<string, string | number | null> | string[]): string

Translates a string with optional placeholder replacement.

Placeholder Types:

  • Object placeholders: {{key}} replaced with placeholders.key
  • Array placeholders: %s replaced in order with array elements
  • Count-based pluralization: When placeholders.count is provided, automatically appends pluralization suffix
has(path: string): Promise<boolean>

Checks if a translation exists for the given path.

Translation Features

Placeholder Replacement

Object-style placeholders:

// Translation: "Hello, {{name}}! You have {{count}} messages."
ctx.lang?.t('greeting', { name: 'John', count: 5 })
// Result: "Hello, John! You have 5 messages."

Array-style placeholders:

// Translation: "%s and %s are friends"
ctx.lang?.t('friendship', ['Alice', 'Bob'])
// Result: "Alice and Bob are friends"

Pluralization

The module automatically handles pluralization based on count values:

  • .zero - for count = 0
  • .one - for count = 1
  • .few - for count = 2, 3, 4
  • .many - for count = 0, 5–20, and other values
// Translations:
// "item.zero": "No items"
// "item.one": "One item"
// "item.few": "{{count}} items"
// "item.many": "{{count}} items"

ctx.lang?.t('item', { count: 0 })  // "No items"
ctx.lang?.t('item', { count: 1 })  // "One item"
ctx.lang?.t('item', { count: 3 })  // "3 items"
ctx.lang?.t('item', { count: 10 }) // "10 items"

Language Resolution

The module supports flexible language resolution:

const i18n = new LanguageController({
  languageResolver: (ctx) => {
    // Single language
    return ctx.from?.language_code ?? 'en';

    // Multiple fallback languages
    return [
      ctx.from?.language_code ?? 'en',
      'en' // fallback
    ];
  }
});

Custom Loaders

You can implement custom language data loaders:

const i18n = new LanguageController({
  loader: async (languageCode: string) => {
    // Load from database, API, etc.
    const data = await loadFromDatabase(languageCode);
    return data;
  },
  ttl: 3600 // Cache for 1 hour
});

TypeScript Support

The module provides full TypeScript support with proper type definitions:

import { LanguageContext } from '@regraf/i18n';

bot.command('start', (ctx: LanguageContext) => {
  // ctx.lang is properly typed
  const message = ctx.lang?.t('welcome') ?? 'Welcome!';
  ctx.reply(message);
});

Testing

Run the test suite:

npm test

The project uses Vitest for testing with comprehensive test coverage.

Requirements

  • Node.js >= 18.0.0
  • Regraf framework

Dependencies

  • debug - Debug logging
  • regraf - Telegram bot framework
  • rgcache - Memory caching

License

ISC

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests at the GitHub repository.

Author

Ilya Petrov [email protected]