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

polingo

v0.0.3

Published

Modern i18n library using .po/.mo files

Readme

Polingo

CI codecov pnpm License: MIT Downloads @polingo/core Downloads @polingo/node Downloads @polingo/web Downloads @polingo/react Downloads @polingo/vue Downloads @polingo/cli Ask DeepWiki

Modern internationalization (i18n) library using industry-standard .po and .mo files for JavaScript/TypeScript projects.

[!CAUTION] Alpha Notice: Polingo is currently in an alpha state. Breaking changes are becoming less common, but anything and everything may still change.

Features

  • Standard Gettext Support: Use .po and .mo files, the industry standard for translations
  • Automatic Catalog Detection: Seamlessly load either .po or .mo catalogs from disk
  • Environment-Agnostic Core: Universal translation engine that works anywhere
  • Node.js Integration: Filesystem loader with optional hot-reload via chokidar
  • Browser Ready: Fetch loader backed by localStorage caching
  • Pluralization: Full support for plural forms across different languages
  • Context Support: Disambiguate identical strings with different meanings
  • Variable Interpolation: Dynamic content with placeholder replacement
  • Middleware Ready: Express and Fastify helpers with locale detection hooks
  • TypeScript First: Strict types, excellent IntelliSense
  • Flexible Caching: Choose in-memory, TTL, or no caching depending on your needs

Packages

Polingo is organized as a monorepo with the following packages:

  • @polingo/core: Environment-agnostic translation engine
  • @polingo/node: Node.js loader with filesystem support and middleware
  • @polingo/web: Browser adapter using fetch + localStorage caching
  • @polingo/react: React bindings with hooks, context provider, and Trans component
  • @polingo/cli: Command-line tools for extraction, compilation, and validation
  • create-polingo-app: Interactive scaffolding for example applications

Coverage

Per-package Codecov coverage (updated on every successful make coverage run in CI):

| Package | Coverage | | --- | --- | | @polingo/core | Coverage @polingo/core | | @polingo/node | Coverage @polingo/node | | @polingo/react | Coverage @polingo/react | | @polingo/vue | Coverage @polingo/vue | | @polingo/web | Coverage @polingo/web |

Installation

Choose the package that fits your environment:

For Node.js applications:

npm install @polingo/node
# or
pnpm add @polingo/node
# or
yarn add @polingo/node

For browser/React applications:

npm install @polingo/core @polingo/web @polingo/react
# or
pnpm add @polingo/core @polingo/web @polingo/react

For CLI tooling (development dependency):

npm install -D @polingo/cli
# or
pnpm add -D @polingo/cli

For environment-agnostic usage:

npm install @polingo/core

Example Templates

Scaffold a working starter directly from the examples/ catalog using the interactive creator:

pnpm create polingo-app

You'll be prompted to pick one of the maintained examples (React + Vite, Express, etc.) and a destination folder. The generator copies the selected template, refreshes the package name, and leaves you ready to install dependencies and start coding.

Quick Start

Basic Usage (Node.js)

import { createPolingo } from '@polingo/node';

const polingo = await createPolingo({
  locale: 'es',
  locales: ['es', 'en', 'fr'],
  directory: './locales',
  fallback: 'en',
});

console.log(polingo.t('Hello')); // "Hola"
console.log(polingo.t('Hello, {name}!', { name: 'Juan' })); // "¡Hola, Juan!"

// Pluralization
console.log(polingo.tn('{n} item', '{n} items', 1, { n: 1 })); // "1 artículo"
console.log(polingo.tn('{n} item', '{n} items', 5, { n: 5 })); // "5 artículos"

Basic Usage (Browser)

import { createPolingo } from '@polingo/web';

const polingo = await createPolingo({
  locale: 'es',
  locales: ['es', 'en'],
  loader: { baseUrl: '/locales' },
});

document.querySelector('#greeting')!.textContent = polingo.t('Hello');

Express Integration

import express from 'express';
import { polingoMiddleware } from '@polingo/node';

const app = express();

app.use(
  polingoMiddleware({
    directory: './locales',
    locales: ['es', 'en', 'fr'],
    fallback: 'en',
  })
);

app.get('/', (req, res) => {
  const greeting = req.polingo.t('Welcome, {name}!', { name: 'User' });
  res.send(greeting);
});

app.listen(3000);

Fastify Integration

import fastify from 'fastify';
import { polingoMiddleware } from '@polingo/node';

const app = fastify();

app.addHook(
  'onRequest',
  polingoMiddleware({
    directory: './locales',
    locales: ['es', 'en', 'fr'],
    fallback: 'en',
  })
);

app.get('/', async (request, reply) => {
  return request.polingo.t('Welcome');
});

app.listen({ port: 3000 });

React Integration

import { PolingoProvider, useTranslation, Trans } from '@polingo/react';
import { createPolingo } from '@polingo/web';

function App() {
  return (
    <PolingoProvider
      create={() =>
        createPolingo({
          locale: 'en',
          locales: ['en', 'es', 'fr'],
          loader: { baseUrl: '/i18n' },
        })
      }
    >
      <MyComponent />
    </PolingoProvider>
  );
}

function MyComponent() {
  const { t, tn, setLocale } = useTranslation();

  return (
    <div>
      <h1>{t('Welcome to Polingo!')}</h1>
      <p>{tn('You have {n} message', 'You have {n} messages', 3, { n: 3 })}</p>
      <Trans
        message="Read the <0>documentation</0> to learn more"
        components={[<a href="/docs" />]}
      />
      <button onClick={() => setLocale('es')}>Español</button>
    </div>
  );
}

For a complete React example, see the React + Vite example.

CLI Workflow

Use the CLI to extract, compile, and validate translations:

# Install CLI tools
pnpm add -D @polingo/cli

# Extract translatable strings from source code (updates ./locales)
# Fuzzy matching is enabled by default to automatically detect similar strings
pnpm polingo extract

# Extract with fuzzy matching disabled
pnpm polingo extract --no-fuzzy

# Extract with custom fuzzy threshold (0-1, default: 0.6)
pnpm polingo extract --fuzzy-threshold 0.8

# After translating .po files, compile to runtime format
pnpm polingo compile locales -o public/i18n --format json

# Validate translations before deployment
pnpm polingo validate locales --strict

Fuzzy Matching: When enabled (default), the extract command automatically detects similar strings between catalog updates and marks them with the #, fuzzy flag, similar to msgmerge behavior. This helps translators identify strings that may need review when source text changes slightly. Obsolete entries (no longer in source) are marked with #~ for reference.

See the @polingo/cli documentation for detailed command reference.

Hot Reload During Development

Enable file watching so catalogs reload automatically when your .po/.mo files change:

const polingo = await createPolingo({
  locale: 'es',
  locales: ['es', 'en'],
  directory: './locales',
  watch: process.env.NODE_ENV === 'development',
  debug: true,
});

// Later, when shutting down:
await polingo.stopWatching?.();

Directory Structure

Your translation files should be organized by locale:

locales/
├── es/
│   └── messages.po
├── en/
│   └── messages.po
└── fr/
    └── messages.po

Translation Methods

  • t(msgid, vars?) - Translate a message
  • tp(context, msgid, vars?) - Translate with context
  • tn(msgid, msgidPlural, count, vars?) - Translate with pluralization
  • tnp(context, msgid, msgidPlural, count, vars?) - Translate with context and pluralization

Configuration

createPolingo(options)

interface CreatePolingoOptions {
  locale: string;        // Initial locale (e.g. 'en')
  locales: string[];     // Locales to preload during startup
  directory: string;     // Path to the locales folder
  fallback?: string;     // Fallback locale when a key is missing (default: 'en')
  domain?: string;       // Translation domain filename prefix (default: 'messages')
  cache?: boolean;       // Use in-memory caching (default: true)
  watch?: boolean;       // Watch .po/.mo files and reload on change (default: false)
  debug?: boolean;       // Log loading and cache activity (default: false)
}

NodeLoader automatically looks for both <locale>/<domain>.po and <locale>/<domain>.mo, preferring .po when both exist.

polingoMiddleware(options)

The middleware shares the same options (minus locale) plus:

  • localeExtractor(req) – customize how the locale is detected (defaults to Accept-Language or ?locale= query parameter).
  • perLocale – set to true to create dedicated translator instances per locale instead of reusing one shared translator.

Development

This project uses pnpm workspaces and includes a Makefile for common tasks. Recommended workflow:

# Install dependencies without touching the lockfile
pnpm install --frozen-lockfile

# Type safety and linting
pnpm typecheck
pnpm lint
pnpm format:check

# Unit tests
pnpm test

Or use the Makefile shortcuts:

# Install dependencies
make install

# Build all packages
make build

# Run tests
make test

# Run tests with coverage
make coverage

# Run linter
make lint

# Clean build artifacts
make clean

Requirements

  • Node.js >= 18.0.0
  • pnpm >= 8.0.0 (developed with pnpm 10.x)

Examples

Check out our working examples to see Polingo in action:

  • React + Vite Example: Full-featured React app demonstrating hooks, Trans component, locale switching, and complete translation workflow

Documentation

For detailed documentation, see the individual package READMEs:

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Security

If you discover a security vulnerability, please follow our security policy for responsible disclosure guidelines.

License

MIT © Reinier Hernández Avila