DEV Community

Abdisamed Mohamed
Abdisamed Mohamed

Posted on

One i18n Engine for CLI, CI, and Edge: How i18nprune Validates Locale JSON with Multi-Runtime SDK and Stable `--json` Output

Intro

Translation keys in source and keys in locale JSON drift apart quietly. Most teams patch it with per-repo scripts, stack-specific plugins, or a TMS they do not want in every pipeline.

i18nprune is an MIT, Node.js 18+ toolkit built around one idea: one deterministic TypeScript core exposed as a CLI, programmatic SDK, and optional hosted report/share apps — with automation-first --json output.

This article covers what actually ships today: multi-runtime hosts, cross-platform CLI behavior, locale layouts, translation providers, safe defaults, and the pattern-based extractor that powers validate/sync/cleanup.


One core, many runtimes

@i18nprune/core owns extraction, cache policy, locale IO, issue codes, and all runXxx operations. Hosts own presentation only.

Host Adapter import Typical tier
CLI / Node scripts @i18nprune/core/runtime/node Read + write locale files
Browser tooling @i18nprune/core/runtime/web Analyze / preview
Workers / edge @i18nprune/core/runtime/edge Analyze + share ingest

The CLI is a thin Commander host. The same validate result shape appears in CI JSON, SDK embeds, and worker-backed share flows — parity snapshot tests guard regressions.


Cross-platform CLI

The installable binary targets Linux, macOS, and Windows:

  • Cache under ~/.i18nprune (or I18NPRUNE_HOME)
  • Documented path semantics and doctor warnings for Windows reserved names and long paths
  • API output uses forward slashes; on-disk IO uses host path.join

Locale layouts and init presets

Three topologies in config:

  • flat_file — e.g. locales/en.json
  • locale_per_dir — e.g. messages/en/common.json
  • feature_bundle — e.g. messages/auth/en.json

i18nprune init --yes scaffolds config with presets for i18next, next-intl, react-intl, lingui, next-i18next, or generic layouts.


Translation providers and safe mutation defaults

Five provider ids ship today: google, deepl, llm, libre, mymemory. Discover with:

i18nprune providers --json
Enter fullscreen mode Exit fullscreen mode

Policy routing and fallback are config-driven. generate --resume fills missing leaves only — not full re-translation.

Safe defaults:

  • cache.profile: balanced (override with safe or fast, or --cache-profile)
  • sync --dry-run previews shape changes
  • Disk writes require global --yes
  • --json runs non-interactively on supported commands

CI: the --json contract

i18nprune validate --json | jq -e '.ok'
Enter fullscreen mode Exit fullscreen mode

Envelope fields: ok, kind, data, issues, meta.apiVersion: "1". Issue codes such as i18nprune.validate.missing_literal_keys are stable API — display copy can change; codes should not.

Extended preflight:

i18nprune doctor --json --strict
Enter fullscreen mode Exit fullscreen mode

Extraction: what we actually built

i18nprune uses an advanced pattern-based extractor (not a single naive regex on t(). Three layers cooperate:

  1. Import binding resolver — expands configured functions from ESM/CJS imports, namespaces (i18n.t), and aliases (t as renameT).
  2. Per-file const maps`${NS}.segment` resolves using constants in that file only (duplicate NS across files does not collide).
  3. Template rebuild + classification — mixed templates fuse static prefixes with runtime holes; dynamic sites are reported, not guessed into locale JSON.

This design targets ~99%+ on literal and const-resolved template keys (near 100% on plain string-literal keys) in typical JS/TS projects while staying conservative on runtime-only expressions — so cleanup and sync do not silently delete keys you still need.

Stack fixtures in the repo exercise Next, Vue, SvelteKit, Nuxt, and Remix patterns.

Roadmap: optional TypeScript AST assist as an opt-in strictness mode on top of the fast default path. IDE extension reuses the same core (coming soon).


Commands you run day to day

Command Role
validate Literal keys in source vs source locale; dynamic site counts
sync Align target locale shape; --dry-run first
missing Scaffold missing paths with placeholders
generate / --resume Provider-backed translation
cleanup Remove keys not in code scan; optional --rg guard
report HTML / JSON / CSV / text project health
share upload Prepared snapshot or report to hosted worker

sync and cleanup measure different datasets — both are documented; counts can diverge by design.


SDK embed (no subprocess)

import { resolveContext, runValidate } from '@i18nprune/core';
import { createNodeRuntimeAdapters } from '@i18nprune/core/runtime/node';

const ctx = await resolveContext({
  projectRoot: process.cwd(),
  adapters: createNodeRuntimeAdapters(),
});

const { payload, issues } = await runValidate(ctx, {});
Enter fullscreen mode Exit fullscreen mode

Subpath exports (/validate, /sync, /generate, …) support smaller bundles. Examples: examples/sdk/ in the repo.


Reports and sharing

i18nprune report --format html --out ./i18n-report.html
i18nprune share upload --project --yes
Enter fullscreen mode Exit fullscreen mode

Hosted viewers: report.i18nprune.dev · web.i18nprune.dev. Worker OpenAPI: worker.i18nprune.dev/docs.


Conclusion

If you need a multi-runtime, cross-platform i18n gate with deterministic JSON for CI, incremental generate, and optional hosted review links — i18nprune is built for that path. Lead with what ships; check the releases portal and git timeline for ongoing changes.

Links

Questions welcome — especially real-world CI integration stories.

Top comments (0)