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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@polingo/cli

v0.0.3

Published

Command line tooling for the Polingo translation workflow

Readme

@polingo/cli

npm version

Command line tooling for the Polingo translation workflow.

The @polingo/cli package provides command-line tools to extract translatable strings from source code, compile .po files to runtime formats, and validate translation catalogs.

Installation

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

Or use directly with npx/pnpm dlx:

pnpm dlx @polingo/cli@latest extract ./src --keep-template

Features

  • Extract: Scans source files for translatable strings and generates .pot template files
    • Recognizes t(), tp(), tn(), tnp() function calls
    • Detects <Trans> component usage in React/JSX files
    • Preserves context and plural forms
    • Syncs locale catalogs under locales/ by default (configure with --locales)
    • Fuzzy matching enabled by default to detect similar strings (like msgmerge)
    • Marks obsolete entries with #~ and similar strings with #, fuzzy
    • Cleans up the temporary POT unless --keep-template is provided
  • Compile: Converts .po files to runtime-ready formats
    • JSON format for @polingo/web (browser usage)
    • Binary .mo format for @polingo/node (server usage)
  • Validate: Lints .po files for quality issues
    • Detects missing translations
    • Identifies fuzzy entries (partially translated)
    • Checks for empty plural forms
    • Strict mode for CI/CD pipelines

Commands

Extract

Scan source code, generate a temporary .pot template, and sync locale catalogs.

polingo extract <source> [options]

Arguments:

  • <source> - Path to source directory or file to scan

Options:

  • -o, --out <path> - Output path for the .pot template file (default: locales/messages.pot)
  • -e, --extensions <exts> - File extensions to scan, comma-separated (default: .ts,.tsx,.js,.jsx)
  • --locales <dir> - Locale root directory; updates/creates per-language .po files alongside the template (default: locales)
  • --languages <codes> - Comma-separated locale codes to ensure exist under --locales
  • --default-locale <code> - Locale whose catalog copies the source strings as translations
  • --fuzzy - Enable fuzzy matching for similar strings (default: enabled)
  • --no-fuzzy - Disable fuzzy matching
  • --fuzzy-threshold <n> - Similarity threshold for fuzzy matching, 0-1 (default: 0.6)
  • --keep-template - Retain the generated .pot file instead of cleaning it up

Examples:

# Extract from src directory (updates ./locales and removes the temporary POT)
polingo extract src

# Keep the temporary POT file for manual review
polingo extract src --keep-template

# Extract to custom output path and retain the template
polingo extract src -o translations/template.pot --keep-template

# Extract from multiple extensions
polingo extract src -e .ts,.tsx,.vue

# Extract from specific file
polingo extract src/components/App.tsx --keep-template

# Extract and sync locale catalogs (create/update locales/en and locales/es)
polingo extract src --locales locales --languages en,es --default-locale en

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

# Extract with custom fuzzy threshold (higher = stricter matching)
polingo extract src --fuzzy-threshold 0.8

What gets extracted:

// Basic translations
t('Hello World')                              // → msgid "Hello World"
t('Hello {name}', { name: 'Alice' })          // → msgid "Hello {name}"

// Context-aware translations
tp('menu', 'File')                            // → msgctxt "menu", msgid "File"

// Plural translations
tn('{n} item', '{n} items', count)            // → msgid "{n} item", msgid_plural "{n} items"

// Context + plural
tnp('cart', '{n} item', '{n} items', count)   // → msgctxt "cart", msgid "{n} item", msgid_plural "{n} items"

// React Trans component
<Trans message="Click <0>here</0>" />         // → msgid "Click <0>here</0>"

When --locales is passed, the extractor mirrors Django's workflow: it creates or updates messages.po under each locale (either detected from the directory or supplied via --languages). The default locale receives the source strings as msgstr values, while other locales get empty placeholders ready for translators.

Fuzzy Matching Behavior:

By default, fuzzy matching is enabled and works similarly to GNU gettext's msgmerge:

  1. Exact matches - Updates are applied with new source references, and any existing #, fuzzy flag is cleared
  2. Similar strings - When a string is slightly modified (e.g., "Save changes" → "Save all changes"), the tool finds the most similar existing translation using Levenshtein distance and:
    • Copies the old translation to the new entry
    • Marks it with #, fuzzy flag for translator review
    • Uses --fuzzy-threshold to control similarity (0.6 = 60% similar, higher = stricter)
  3. Obsolete entries - Strings no longer found in source code are marked with #~ comment for reference

Disable fuzzy matching with --no-fuzzy to get the old behavior where modified strings create new empty entries.

The intermediate messages.pot is written to the locales directory and removed at the end of the run unless --keep-template is provided.

Compile

Convert .po translation files to runtime formats (JSON or binary .mo).

polingo compile <source> [options]

Arguments:

  • <source> - Path to directory containing locale folders with .po files

Options:

  • -o, --out <path> - Output directory for compiled catalogs (default: dist/locales)
  • -f, --format <format> - Output format: json or mo (default: json)

Examples:

# Compile to JSON (for @polingo/web)
polingo compile locales -o public/i18n --format json

# Compile to .mo files (for @polingo/node)
polingo compile locales -o dist/locales --format mo

# Compile with default settings
polingo compile locales

Expected directory structure:

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

Output structure (JSON):

public/i18n/
├── en/
│   └── messages.json
├── es/
│   └── messages.json
└── fr/
    └── messages.json

Validate

Lint and validate .po translation files for completeness and quality.

polingo validate <source> [options]

Arguments:

  • <source> - Path to directory containing locale folders with .po files

Options:

  • --strict - Fail validation if any issues are found (useful for CI/CD)

Examples:

# Basic validation with warnings
polingo validate locales

# Strict validation (exit code 1 if issues found)
polingo validate locales --strict

# Validate specific locale
polingo validate locales/es

What gets validated:

  • Missing translations (empty msgstr)
  • Fuzzy entries (marked with #, fuzzy comment)
  • Invalid plural forms
  • Malformed .po file syntax

Workflow Integration

NPM Scripts

Add these scripts to your package.json:

{
  "scripts": {
    "i18n:extract": "polingo extract src -o locales/messages.pot",
    "i18n:compile": "polingo compile locales -o public/i18n --format json",
    "i18n:validate": "polingo validate locales --strict",
    "prebuild": "npm run i18n:extract && npm run i18n:compile && npm run i18n:validate",
    "build": "vite build"
  }
}

Continuous Integration

Example GitHub Actions workflow:

name: Validate Translations
on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'pnpm'
      - run: pnpm install --frozen-lockfile
      - run: pnpm polingo validate locales --strict

Programmatic API

You can also use the CLI programmatically:

import { extract, compile, validate } from '@polingo/cli';

// Extract strings
await extract({
  source: './src',
  out: './locales/messages.pot',
  extensions: ['.ts', '.tsx'],
});

// Compile catalogs
await compile({
  source: './locales',
  out: './public/i18n',
  format: 'json',
});

// Validate catalogs
const result = await validate({
  source: './locales',
  strict: true,
});

if (!result.valid) {
  console.error('Validation failed:', result.errors);
  process.exit(1);
}

Related packages

  • @polingo/core – translation runtime shared by all adapters.
  • @polingo/node – filesystem loader, middleware, and watcher for Node.js environments.
  • @polingo/web – fetch-based loader with browser-friendly caching.
  • @polingo/react – React hooks, context provider, and Trans component.

License

MIT © Reinier Hernández Avila