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

@lerianstudio/sindarian-i18n-cli

v1.0.0-beta.1

Published

i18n message extraction, validation, and key diffing CLI for Sindarian projects

Readme

@lerianstudio/sindarian-i18n-cli

i18n message extraction, validation, and key diffing CLI + library for React/TypeScript projects using react-intl.

Part of the Lerian Studio console-sdk monorepo.

✨ Features

  • Extract i18n messages from source files — supports defineMessages(), <FormattedMessage>, intl.formatMessage(), and custom function names (e.g. $t())
  • Validate extracted messages — detects missing IDs, missing defaultMessage, and duplicate IDs with conflicting text
  • Diff keys against locale JSON files — finds new untranslated keys and stale keys no longer in source
  • Report issues in ESLint-style grouped output with file:line:col and ANSI colors

📦 Installation

npm install @lerianstudio/sindarian-i18n-cli

Or as a dev dependency:

npm install --save-dev @lerianstudio/sindarian-i18n-cli

🚀 Quick start

1. Create a config file

Create sindarian-i18n.config.ts in your project root:

import type { I18nConfig } from '@lerianstudio/sindarian-i18n-cli'

const config: I18nConfig = {
  filePatterns: ['./src/**/!(*.d).{ts,tsx}'],
  additionalFunctionNames: ['$t'],
  defaultLocale: 'en',
  locales: ['en', 'pt'],
  localeDir: './locales/extracted',
  concurrency: 10
}

export default config

2. Extract messages

npx sindarian-i18n extract

This scans your source files, extracts all i18n messages, and writes locale JSON files to the configured localeDir. The default locale file contains { "message.id": "Default message text" } pairs. Other locale files are merged — existing translations are preserved, new keys get empty strings, and stale keys are removed.

3. Validate messages

npx sindarian-i18n check

Exits with code 1 if any errors are found (missing IDs, missing defaultMessage, duplicate IDs with different text).

4. Check key sync

npx sindarian-i18n check-keys

Exits with code 1 if the locale file is out of sync with your source code. Reports which keys were added or removed.


🔧 CLI reference

sindarian-i18n [options] <command>

Options:
  -c, --config <path>   Path to config file
  -V, --version         Output the version number
  -h, --help            Display help

Commands:
  extract               Extract i18n messages and write locale files
  check                 Validate extracted i18n messages (exits 1 on errors)
  check-keys            Diff extracted keys against locale file (exits 1 if out of sync)

extract

Scans files matching filePatterns, extracts messages, validates them, and writes locale JSON files. If extraction or validation errors occur, they are printed but extraction still completes for valid files.

check

Runs extraction and validation without writing any files. Exits with code 1 on errors — useful as a CI gate.

check-keys

Runs extraction, then compares the extracted keys against the default locale JSON file. Exits with code 1 if keys are out of sync. Tells you to run extract when new keys are found.


⚙️ Configuration

The CLI auto-detects config files in this order:

  1. sindarian-i18n.config.ts
  2. sindarian-i18n.config.js
  3. intl.config.ts

You can override this with the -c / --config flag.

Config options

| Option | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | filePatterns | string[] | Yes | — | Glob patterns to scan for i18n messages | | additionalFunctionNames | string[] | No | [] | Extra function names to extract besides defineMessages / FormattedMessage / formatMessage | | defaultLocale | string | Yes | — | Source-of-truth locale code (e.g. 'en') | | locales | string[] | Yes | — | All locale codes including defaultLocale (e.g. ['en', 'pt']) | | localeDir | string | Yes | — | Directory where locale JSON files are stored | | concurrency | number | No | 10 | Max file-read concurrency during extraction |


🔌 Programmatic API

All core functions are exported for use in scripts, custom tooling, or build pipelines.

import {
  extractAll,
  extractFile,
  formatSimpleJson,
  validate,
  diffKeys,
  formatValidationReport,
  formatExtractionErrors,
  formatKeyDiffReport,
  loadConfig,
  loadConfigFromFile,
  findConfigFile
} from '@lerianstudio/sindarian-i18n-cli'

Extraction

import { extractAll, formatSimpleJson } from '@lerianstudio/sindarian-i18n-cli'

const result = await extractAll({
  filePatterns: ['./src/**/*.tsx'],
  additionalFunctionNames: ['$t'],
  concurrency: 10
})

// result.messages  — Map<string, ResolvedMessage> (deduplicated by id)
// result.rawMessages — Map<string, ResolvedMessage[]> (grouped by file)
// result.errors — ExtractionError[]

const json = formatSimpleJson(result.messages)
// '{ "app.title": "My App", "app.greeting": "Hello, {name}!" }'

Validation

import { extractAll, validate, formatValidationReport } from '@lerianstudio/sindarian-i18n-cli'

const result = await extractAll({ filePatterns: ['./src/**/*.tsx'] })
const validation = validate(result)

if (validation.errorCount > 0) {
  console.error(formatValidationReport(validation))
  process.exit(1)
}

Key diffing

import { extractAll, diffKeys, formatKeyDiffReport } from '@lerianstudio/sindarian-i18n-cli'

const result = await extractAll({ filePatterns: ['./src/**/*.tsx'] })
const diff = await diffKeys(result.messages, './locales/extracted/en.json')

if (diff.added.length > 0 || diff.removed.length > 0) {
  console.error(formatKeyDiffReport(diff))
}

Config loading

import { loadConfig, findConfigFile } from '@lerianstudio/sindarian-i18n-cli'

// Auto-detect and load config
const config = loadConfig()

// Or load from a specific path
import { loadConfigFromFile } from '@lerianstudio/sindarian-i18n-cli'
const config2 = loadConfigFromFile('./my-custom-config.ts')

Exported types

import type {
  I18nConfig,
  ResolvedMessage,
  ExtractionResult,
  ExtractionError,
  ExtractorConfig,
  ValidationResult,
  ValidationIssue,
  IssueSeverity,
  KeyDiffResult
} from '@lerianstudio/sindarian-i18n-cli'

🤖 CI integration

Add validation and key-sync checks to your CI pipeline to catch i18n issues before merge.

GitHub Actions example

- name: Check i18n messages
  run: npx sindarian-i18n check

- name: Check i18n key sync
  run: npx sindarian-i18n check-keys

npm scripts

{
  "scripts": {
    "i18n:extract": "sindarian-i18n extract",
    "i18n:check": "sindarian-i18n check",
    "i18n:check-keys": "sindarian-i18n check-keys"
  }
}

Both check and check-keys exit with code 1 on failure, so they work out of the box as CI gates.


📄 License

ISC