<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abdisamed Mohamed</title>
    <description>The latest articles on DEV Community by Abdisamed Mohamed (@zamdevio).</description>
    <link>https://dev.to/zamdevio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3982400%2F3f31640f-b4e7-4c75-a7a0-964778ce11ad.png</url>
      <title>DEV Community: Abdisamed Mohamed</title>
      <link>https://dev.to/zamdevio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zamdevio"/>
    <language>en</language>
    <item>
      <title>One i18n Engine for CLI, CI, and Edge: How i18nprune Validates Locale JSON with Multi-Runtime SDK and Stable `--json` Output</title>
      <dc:creator>Abdisamed Mohamed</dc:creator>
      <pubDate>Sat, 13 Jun 2026 14:04:08 +0000</pubDate>
      <link>https://dev.to/zamdevio/one-i18n-engine-for-cli-ci-and-edge-how-i18nprune-validates-locale-json-with-multi-runtime-sdk-4jce</link>
      <guid>https://dev.to/zamdevio/one-i18n-engine-for-cli-ci-and-edge-how-i18nprune-validates-locale-json-with-multi-runtime-sdk-4jce</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  One core, many runtimes
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@i18nprune/core&lt;/code&gt; owns extraction, cache policy, locale IO, issue codes, and all &lt;code&gt;runXxx&lt;/code&gt; operations. Hosts own presentation only.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;Adapter import&lt;/th&gt;
&lt;th&gt;Typical tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLI / Node scripts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@i18nprune/core/runtime/node&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read + write locale files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser tooling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@i18nprune/core/runtime/web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Analyze / preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workers / edge&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@i18nprune/core/runtime/edge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Analyze + share ingest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cross-platform CLI
&lt;/h2&gt;

&lt;p&gt;The installable binary targets &lt;strong&gt;Linux, macOS, and Windows&lt;/strong&gt;:&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Locale layouts and init presets
&lt;/h2&gt;

&lt;p&gt;Three topologies in config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;flat_file&lt;/code&gt; — e.g. &lt;code&gt;locales/en.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;locale_per_dir&lt;/code&gt; — e.g. &lt;code&gt;messages/en/common.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feature_bundle&lt;/code&gt; — e.g. &lt;code&gt;messages/auth/en.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;i18nprune init --yes&lt;/code&gt; scaffolds config with presets for &lt;strong&gt;i18next&lt;/strong&gt;, &lt;strong&gt;next-intl&lt;/strong&gt;, &lt;strong&gt;react-intl&lt;/strong&gt;, &lt;strong&gt;lingui&lt;/strong&gt;, &lt;strong&gt;next-i18next&lt;/strong&gt;, or generic layouts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Translation providers and safe mutation defaults
&lt;/h2&gt;

&lt;p&gt;Five provider ids ship today: &lt;strong&gt;google&lt;/strong&gt;, &lt;strong&gt;deepl&lt;/strong&gt;, &lt;strong&gt;llm&lt;/strong&gt;, &lt;strong&gt;libre&lt;/strong&gt;, &lt;strong&gt;mymemory&lt;/strong&gt;. Discover with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;i18nprune providers &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Policy routing and fallback are config-driven. &lt;code&gt;generate --resume&lt;/code&gt; fills &lt;strong&gt;missing leaves only&lt;/strong&gt; — not full re-translation.&lt;/p&gt;

&lt;p&gt;Safe defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cache.profile: balanced&lt;/code&gt; (override with &lt;code&gt;safe&lt;/code&gt; or &lt;code&gt;fast&lt;/code&gt;, or &lt;code&gt;--cache-profile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sync --dry-run&lt;/code&gt; previews shape changes&lt;/li&gt;
&lt;li&gt;Disk writes require global &lt;code&gt;--yes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--json&lt;/code&gt; runs non-interactively on supported commands&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  CI: the &lt;code&gt;--json&lt;/code&gt; contract
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;i18nprune validate &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'.ok'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Extended preflight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;i18nprune doctor &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Extraction: what we actually built
&lt;/h2&gt;

&lt;p&gt;i18nprune uses an &lt;strong&gt;advanced pattern-based extractor&lt;/strong&gt; (not a single naive regex on &lt;code&gt;t(&lt;/code&gt;). Three layers cooperate:&lt;/p&gt;

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

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

&lt;p&gt;Stack fixtures in the repo exercise Next, Vue, SvelteKit, Nuxt, and Remix patterns.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Commands you run day to day
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Literal keys in source vs source locale; dynamic site counts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Align target locale shape; &lt;code&gt;--dry-run&lt;/code&gt; first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;missing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scaffold missing paths with placeholders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;generate&lt;/code&gt; / &lt;code&gt;--resume&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Provider-backed translation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cleanup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove keys not in code scan; optional &lt;code&gt;--rg&lt;/code&gt; guard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;report&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTML / JSON / CSV / text project health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;share upload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prepared snapshot or report to hosted worker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;cleanup&lt;/code&gt; measure different datasets — both are documented; counts can diverge by design.&lt;/p&gt;




&lt;h2&gt;
  
  
  SDK embed (no subprocess)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolveContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runValidate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@i18nprune/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createNodeRuntimeAdapters&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@i18nprune/core/runtime/node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;resolveContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;projectRoot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;adapters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createNodeRuntimeAdapters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;issues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runValidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subpath exports (&lt;code&gt;/validate&lt;/code&gt;, &lt;code&gt;/sync&lt;/code&gt;, &lt;code&gt;/generate&lt;/code&gt;, …) support smaller bundles. Examples: &lt;code&gt;examples/sdk/&lt;/code&gt; in the repo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reports and sharing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;i18nprune report &lt;span class="nt"&gt;--format&lt;/span&gt; html &lt;span class="nt"&gt;--out&lt;/span&gt; ./i18n-report.html
i18nprune share upload &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hosted viewers: &lt;a href="https://report.i18nprune.dev" rel="noopener noreferrer"&gt;report.i18nprune.dev&lt;/a&gt; · &lt;a href="https://web.i18nprune.dev" rel="noopener noreferrer"&gt;web.i18nprune.dev&lt;/a&gt;. Worker OpenAPI: &lt;a href="https://worker.i18nprune.dev/docs" rel="noopener noreferrer"&gt;worker.i18nprune.dev/docs&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you need a &lt;strong&gt;multi-runtime&lt;/strong&gt;, &lt;strong&gt;cross-platform&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zamdevio/i18nprune" rel="noopener noreferrer"&gt;https://github.com/zamdevio/i18nprune&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.i18nprune.dev" rel="noopener noreferrer"&gt;https://docs.i18nprune.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Home: &lt;a href="https://i18nprune.dev" rel="noopener noreferrer"&gt;https://i18nprune.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Web workspace: &lt;a href="https://web.i18nprune.dev" rel="noopener noreferrer"&gt;https://web.i18nprune.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Report viewer: &lt;a href="https://report.i18nprune.dev" rel="noopener noreferrer"&gt;https://report.i18nprune.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Worker API: &lt;a href="https://worker.i18nprune.dev/docs" rel="noopener noreferrer"&gt;https://worker.i18nprune.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Releases: &lt;a href="https://releases.i18nprune.dev" rel="noopener noreferrer"&gt;https://releases.i18nprune.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Build timeline: &lt;a href="https://git.i18nprune.dev" rel="noopener noreferrer"&gt;https://git.i18nprune.dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions welcome — especially real-world CI integration stories.&lt;/p&gt;

</description>
      <category>i18n</category>
      <category>typescript</category>
      <category>cli</category>
      <category>ci</category>
    </item>
  </channel>
</rss>
