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

json-i18n-editor

v0.9.0

Published

Local browser-based editor for JSON translation files. Spreadsheet UI, zero dependencies.

Downloads

614

Readme

json-i18n-editor

A local browser-based editor and auditor for your JSON translation files. No cloud, no account, zero dependencies.

  • Edit — spreadsheet-style UI: rows = keys, columns = languages. Inline editing, search, CSV import/export, missing-translation highlights, per-language completeness.
  • Audit — scans your source code for translation calls and cross-references them against your JSON files: keys used in code but missing from JSON (the ones that silently break your UI), and keys in JSON never used in code. Works with any framework.
  • Online mode — run it on a server with --password and edit translations from anywhere: login screen, session cookies, no extra setup.

Quick start

cd your-project
npx json-i18n-editor init     # auto-detects your setup, writes i18n.scan.json
npx json-i18n-editor          # opens the editor UI
npx json-i18n-editor audit    # terminal audit — exit 1 if keys are missing (CI-ready)

Or install as a dev dependency and add scripts:

{
  "scripts": {
    "i18n": "json-i18n-editor",
    "audit:i18n": "json-i18n-editor audit"
  }
}

The editor

  1. Reads all *.json files from the locales directory
  2. Opens http://localhost:3737 in your browser
  3. Edit inline, add/delete/rename keys, filter, press Save or Ctrl+S
  4. Writes back to disk preserving nested structure
  5. The 🔍 Audit button shows scan results in-app: add a missing key with one click, delete unused ones

Expected layout — one JSON file per language, flat or nested:

locales/
  en.json      { "hero": { "title": "Hello" } }
  es.json      { "hero": { "title": "Hola" } }

The audit

json-i18n-editor audit scans your source files for translation calls and reports:

  ❌  2 missing keys (used in code, not in any JSON):
      hero.badge    src/pages/index.astro:14
      footer.phone  src/layouts/Layout.astro:92

  ⚠   6 unused keys (in JSON, never used in code)
  ⚠   2 dynamic key calls — the unused list may be incomplete
  ⚠   es: 3 untranslated keys

Exit code 1 when keys are missing, 0 otherwise — drop audit:i18n into CI to block deploys with broken translations.

Recognized out of the box: t("key"), t(lang, "key"), $t("key") (Vue/Nuxt), i18n.t("key"), translate("key"), 'key' | translate (Angular). Keys built dynamically (t(`item_${i}`)) can't be resolved statically; they're counted and reported.

Namespace wrappers are resolved automatically. If a file defines a local helper that prefixes a namespace —

const t = (key: string) => translation(`LOGIN.${key}`)   // template literal
const tc = (key: string) => translation("COMMON." + key) // string concat, useCallback(...) too

— then t("USERNAME") is correctly audited as LOGIN.USERNAME, not USERNAME. A wrapper is trusted only when its namespace exists in your JSON files or its inner function is provably i18n (t, translate, or aliased from useTranslation()/useI18n()), so lookalike functions never produce false positives.

Online / production mode

Run the editor on a server (VPS, staging box, LAN machine) and protect it with a password:

npx json-i18n-editor --password mysecret
# or, keep the password out of shell history:
JSON_I18N_PASSWORD=mysecret npx json-i18n-editor

Anyone opening the URL gets a login screen; every API call (read, save, import, audit) requires the session cookie. Sessions live in memory — restarting the server logs everyone out. Wrong attempts are throttled.

Works with any project layout — same --dir/config resolution as local mode. The editor is a plain HTTP server on a port: use it directly, or put any reverse proxy of your choice in front for TLS — it needs zero configuration to sit behind one.

Brand it for a client with --title (or the JSON_I18N_TITLE env var):

npx json-i18n-editor --password mysecret --title "Acme Corp — Translations"

The page title, topbar and login screen show your title instead of the product name.

Recipe: edit production translations live (docker compose)

A pattern that works with any stack whose app fetches the translation JSON at runtime (i18next-http-backend and friends): run the editor as a sidecar next to your web container, and let both share the same host folder — the one in your repo checkout.

services:
  web:
    # ... your production web server, unchanged ...
    volumes:
      # Serve the host copy of the translations instead of the one baked
      # into the image → edits go live on refresh, no rebuild, no redeploy.
      - ./public/translations:/usr/share/nginx/html/translations:ro

  i18n-editor:
    image: node:20-alpine
    working_dir: /workspace
    command: npx -y [email protected] --title "Acme Corp — Translations"
    environment:
      - JSON_I18N_PASSWORD=${I18N_PASSWORD}   # from your .env
    volumes:
      - .:/workspace   # the repo checkout: config, sources (for audit) and translations
    ports:
      - "3737:3737"    # → http://your-server:3737
    restart: always

That's the whole setup: translators open http://your-server:3737, log in, edit, hit save — the app shows the new texts on the next page load, and the files land in the repo checkout on the server (pull them back into git whenever it suits your workflow).

Want a proper domain with TLS? Drop the ports: block and route the service through whatever reverse proxy you already run — the editor needs zero configuration to sit behind one. With Traefik, that's labels on the sidecar (Host(`translate.example.com`) → service port 3737); with nginx or Caddy, a one-location proxy_pass http://i18n-editor:3737; server block. Any proxy that can forward HTTP works — the editor doesn't know or care which one is in front.

init — works with any stack

npx json-i18n-editor init writes i18n.scan.json by detecting:

  • Your locales folder — searches for directories of <lang>.json files
  • Your framework — package.json dependencies → which file extensions to scan
  • Your custom helpers — any function called with 3+ of your existing keys as string literals gets a generated pattern automatically. No framework knowledge needed: your keys are the ground truth. Discovers things like getT(lang, "key"), $_("key") (svelte-i18n), .instant("key") (ngx-translate), __("key")
{
  "dir": "src/i18n/locales",
  "scanDir": "src",
  "extensions": [".astro", ".ts", ".tsx"],
  "patterns": ["auto", "(?<![\\w$.])getT\\(\\s*[\\w$.]+\\s*,\\s*[\"'`]([\\w.-]+)[\"'`]"],
  "ignore": ["node_modules", "dist", ".git", ".test."]
}

"auto" expands to the built-in patterns; add your own regexes (capture group 1 = the key) for anything exotic. Config can also live in a "json-i18n-editor" field in package.json. With "dir" set, no flags are needed for any command.

Options

json-i18n-editor [command] [options]

Commands:
  (none)   Open the editor UI
  audit    Terminal audit, no browser — exit 1 on missing keys
  init     Detect project setup, write i18n.scan.json

Options:
  --dir <path>    Locales folder (default: "dir" from config, else ./messages)
  --port <port>   UI port (default: 3737)
  --scan <path>   Source dir to scan (default: from config, else ./src)
  --force         init only: overwrite existing i18n.scan.json

License

MIT