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

@bemedev/i18n

v0.5.2

Published

Internationalization library for Bemedev projects, providing utilities for managing translations and locale-specific content.

Readme

@bemedev/i18n

A modern, fully-typed internationalization (i18n) library for TypeScript and JavaScript with a fluent, composable API. Define once, compose per-lo// Locales are narrowed from machine.keys const call = machine.translate('greetings', { name: 'A' }).to; // call: (locale?: 'en' | 'es-ES' | 'en-US') => string


### Utility types

If you need to extract types from your i18n machine, several utility types
are provided:

```ts
import type {
  ConfigFrom,        // Extract the configuration type
  KeysFrom,          // Extract available locale keys
  TranslationsFrom,  // Extract all translations
  TranslationFrom,   // Extract a single locale translation
  KeyFrom,           // Extract the current locale key
} from '@bemedev/i18n/class';

import type {
  RequiredTranslations,  // Make all translations required
  _Translations,         // Make all translations optional
  _Params,              // Extract parameters for a translation key
  CheckParams,          // Validate parameter requirements
} from '@bemedev/i18n/types';

// Example usage
type Config = ConfigFrom<typeof machine>;
type Locales = KeysFrom<typeof machine>;
type AllTranslations = TranslationsFrom<typeof machine>;
type EnglishTranslation = TranslationFrom<typeof machine>;

// Extract parameters for a specific key
type GreetingParams = _Params<Config, 'greetings'>;
// => { name: string; lastLoginDate: Date }

Note: Properties prefixed with __ (like __key, __translation) are internal and marked as deprecated. They exist only for type inference and should not be used at runtime.te safely with strong types.

Features

  • ✅ Strong TypeScript typing for keys and params
  • ✅ Simple, fluent API with a class-like builder
  • ✅ Formatting helpers: date, number, plural, list, enum
  • ✅ Nested structures with dot-path access
  • ✅ Locale fallback (en, en-US ➜ en)
  • ✅ Zero-deps runtime, tiny footprint

Installation

# npm
npm install @bemedev/i18n

# yarn
yarn add @bemedev/i18n

# pnpm
pnpm add @bemedev/i18n

Quick start

import { create } from '@bemedev/i18n';

// Create a machine with a base locale and a default fallback
const machine = create(
  dt => ({
    localee: 'en',
    greetings: 'Hello {name}! Your last login was {lastLoginDate:date}.',
    inboxMessages: dt('Hello {name}, you have {messages:plural}.', {
      plural: { messages: { one: '1 message', other: '{?} messages' } },
    }),
    nested: {
      greetings: dt('Hello {names:list}!', {
        list: { names: { style: 'short' } },
      }),
    },
  }),
  'en',
)
  .provideTranslation('es-ES', dt => ({
    localee: 'es-ES',
    greetings: dt(
      '¡Hola {name}! Tu última conexión fue el {lastLoginDate:date}.',
      {
        date: {
          lastLoginDate: {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
          },
        },
      },
    ),
  }))
  .provideTranslation('en-US', { localee: 'en-US' });

// Translate
const msg = machine
  .translate('greetings', {
    name: 'John',
    lastLoginDate: new Date('2023-10-01T12:00:00Z'),
  })
  .to('es-ES');
// => "¡Hola John! Tu última conexión fue el 01/10/2023."

API overview

  • create(config | define => config, ...fallbacks)
    • Returns an I18n machine with methods:
      • provideTranslation(locale, value | define => value): chain new locale entries
      • translate(key, args?).to(locale?): lazy translator returning a string
      • translateWithLocale(locale, { key, args? }): direct translation function
      • keys: string[] of known locales
      • translations: resolved map of locale ➜ messages

Defining translations

You can write plain messages or use dt to attach formatting options.

const machine = create(
  dt => ({
    greetings: 'Hello {name}!',
    status: dt('Order is {status:enum}', {
      enum: { status: { pending: 'pending', shipped: 'shipped' } },
    }),
    lastSeen: dt('Last seen: {date:date}', {
      date: { date: { dateStyle: 'long' } },
    }),
    items: dt('You have {count:plural}', {
      plural: { count: { one: '1 item', other: '{?} items' } },
    }),
    friends: dt('Online: {users:list}', {
      list: { users: { style: 'long', type: 'conjunction' } },
    }),
  }),
  'en',
);

Using translate vs translateWithLocale

// Lazy: bind key/args, choose locale later
const invite = machine.translate('greetings', { name: 'Ada' });
invite.to('en');
invite.to('es-ES');

// Direct: provide locale immediately
machine.translateWithLocale('en', {
  key: 'greetings',
  args: { name: 'Ada' },
});

Nested keys and arrays

Dot-paths are supported for deep access; non-string values (objects/arrays) are returned as-is when defined without dt.

const m = create(
  {
    nested: {
      data: { lang: 'en', langs: ['fr', 'gb', 'es'] },
      someArray: ['string1', 'string2'],
    },
  },
  'en',
);

m.translate('nested.data').to('en'); // => { lang: 'en', langs: ['fr','gb','es'] }
m.translate('nested.someArray').to('en'); // => ['string1', 'string2']

Type safety

Types are inferred from your config and preserved per-locale.

// Keys and params are type-checked
machine.translate('greetings', {
  name: 'John',
  lastLoginDate: new Date(),
}); // ✅
machine.translate('greetings'); // ❌ missing 'name'
machine.translate('unknown.key'); // ❌ unknown key

// Locales are narrowed from machine.keys
const call = machine.translate('greetings', { name: 'A' }).to;
// // call: (locale?: 'en' | 'es-ES' | 'en-US') => string

Utility types

If you need to extract types from your i18n machine, several utility types are provided:

import type {
  ConfigFrom, // Extract the configuration type
  KeysFrom, // Extract available locale keys
  TranslationsFrom, // Extract all translations
  TranslationFrom, // Extract a single locale translation
  KeyFrom, // Extract the current locale key
} from '@bemedev/i18n/class';

import type {
  RequiredTranslations, // Make all translations required
  _Translations, // Make all translations optional
  _Params, // Extract parameters for a translation key
  CheckParams, // Validate parameter requirements
} from '@bemedev/i18n/types';

// Example usage
type Config = ConfigFrom<typeof machine>;
type Locales = KeysFrom<typeof machine>;
type AllTranslations = TranslationsFrom<typeof machine>;
type EnglishTranslation = TranslationFrom<typeof machine>;

// Extract parameters for a specific key
type GreetingParams = _Params<Config, 'greetings'>;
// => { name: string; lastLoginDate: Date }

Note: Properties prefixed with __ (like __key, __translation) are internal and marked as deprecated. They exist only for type inference and should not be used at runtime.


Advanced Usage


If you want to re-use the machine’s internal types, utility types are
provided:

```ts
import type {
  ConfigFrom,
  KeysFrom,
  KeyFrom, // @deprecated – internal typing only
  TranslationsFrom,
  TranslationFrom, // @deprecated – internal typing only
} from '@bemedev/i18n/class';

Note: properties prefixed by ** (like **key, \_\_translation) are marked as deprecated and exist only to carry types. Don’t use them at runtime.


Advanced Usage

Creating translations from existing configurations

If you want to create a new translation based on an existing configuration without copying the entire object, use the translation helper:

Basic usage with translation

import { translation } from '@bemedev/i18n';

const translate = translation(
  {
    greeting: 'Hello',
    farewell: 'Goodbye',
    withParam: 'Hello {name}!',
    nested: {
      welcome: 'Welcome back',
      deep: {
        message: 'Deep message',
      },
    },
  },
  'en',
);

// Simple translations
translate('greeting'); // => 'Hello'
translate('farewell'); // => 'Goodbye'

// With parameters
translate('withParam', { name: 'John' }); // => 'Hello John!'

// Nested keys
translate('nested.welcome'); // => 'Welcome back'
translate('nested.deep.message'); // => 'Deep message'

Using translation.derived for type-safe derived translations

Create translations that derive from an existing configuration while maintaining full type safety:

import { translation } from '@bemedev/i18n';
import { machine } from './your-base-config';

const spanishTranslation = translation.derived<typeof machine.config>(
  dt => ({
    localee: 'es-ES',
    greetings: dt(
      '¡Hola {name}! Tu última conexión fue el {lastLoginDate:date}.',
      {
        date: {
          lastLoginDate: {
            month: '2-digit',
            year: 'numeric',
            day: '2-digit',
          },
        },
      },
    ),
    nested: {
      greetings: dt('¡Hola {names:list}!'),
    },
  }),
  'es-ES',
);

// Fully typed translations
spanishTranslation('greetings', {
  name: 'Juan',
  lastLoginDate: new Date('2024-06-15T12:00:00Z'),
});
// => '¡Hola Juan! Tu última conexión fue el 15/06/2024.'

Using translation.fromMachine for machine-based translations

Create translations directly from an existing i18n machine:

import { translation } from '@bemedev/i18n';
import { machine } from './your-machine';

const germanTranslation = translation.fromMachine<typeof machine>(
  dt => ({
    localee: 'de-DE',
    greetings: dt(
      'Hallo {name}! Deine letzte Anmeldung war {lastLoginDate:date}.',
    ),
    // ... other translations
  }),
  'de-DE',
);

Accessing the configuration

The translation function returns an object with a config property that exposes the underlying configuration:

const translate = translation({ greeting: 'Hello' }, 'en');
console.log(translate.config); // => { greeting: 'Hello' }

Licence

MIT

CHANGE_LOG

Acknowledgements

Special thanks to all contributors, testers, and users who provided feedback and helped improve this library. Your support and suggestions are greatly appreciated!

This was inspired by Web Dev Simplified

The Youtube video that inspired this library can be found here.

Author

chlbri ([email protected])

My github

Mainfull Links