<?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>Forem: Mohammed Hassan </title>
    <description>The latest articles on Forem by Mohammed Hassan  (@matrix2526).</description>
    <link>https://forem.com/matrix2526</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%2F320685%2F28b69267-4353-4f3a-aecc-b1edab70a75f.jpeg</url>
      <title>Forem: Mohammed Hassan </title>
      <link>https://forem.com/matrix2526</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/matrix2526"/>
    <language>en</language>
    <item>
      <title>How I built a browser watermark library that survives DevTools</title>
      <dc:creator>Mohammed Hassan </dc:creator>
      <pubDate>Sat, 09 May 2026 11:56:32 +0000</pubDate>
      <link>https://forem.com/matrix2526/how-i-built-a-browser-watermark-library-that-survives-devtools-56ec</link>
      <guid>https://forem.com/matrix2526/how-i-built-a-browser-watermark-library-that-survives-devtools-56ec</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Identifying the leaker after a screenshot escapes, when the obvious tricks no longer work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Last year, I watched a customer of ours forward a screenshot of an internal dashboard to a vendor. The screenshot showed financial figures that should have stayed inside the company. Nobody could prove who took it.&lt;/p&gt;

&lt;p&gt;If every pixel of that screen had carried that user's email or ID in faint diagonal text, we would have known the answer in five seconds.&lt;/p&gt;

&lt;p&gt;That is the whole idea behind a watermark. It does not prevent leaks. It makes leaks attributable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnidi8h627jyptt90uj9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnidi8h627jyptt90uj9.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why naive watermarks fail
&lt;/h2&gt;

&lt;p&gt;The simplest version is a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; you put on top of the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"watermark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;alice@acme.dev&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trouble: anyone who knows F12 opens DevTools and deletes the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; in two clicks. Or overrides its CSS. Or screenshots the page before your watermark script runs.&lt;/p&gt;

&lt;p&gt;The naive watermark catches honest users. It does nothing against the people who actually want to leak.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wanted
&lt;/h2&gt;

&lt;p&gt;A library that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Renders the user's identifier (email, user id, whatever) tiled across the page.&lt;/li&gt;
&lt;li&gt;Re-creates itself if someone removes it from the DOM.&lt;/li&gt;
&lt;li&gt;Resists CSS overrides that try to hide it.&lt;/li&gt;
&lt;li&gt;Optionally redirects users who open DevTools to a "you do not have access to this" page.&lt;/li&gt;
&lt;li&gt;Logs every attempt to my server. Even if every browser-side defense gets bypassed, I want a record of who tried what, when.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last one is the part most teams forget, and it is the most important.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;watermark-shield&lt;/code&gt; is a small library on top of &lt;a href="https://github.com/zhensherlock/watermark-js-plus" rel="noopener noreferrer"&gt;watermark-js-plus&lt;/a&gt;, which handles the actual canvas rendering very nicely. On top of it I added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A self-healing overlay that comes back if removed.&lt;/li&gt;
&lt;li&gt;A MutationObserver that catches CSS injection attempts on the watermark.&lt;/li&gt;
&lt;li&gt;A DevTools-detection layer that fires a callback before it redirects.&lt;/li&gt;
&lt;li&gt;An audit hook so the server can log attempts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Framework-agnostic core in plain JavaScript, with bindings for Angular, React, and Vue 3.&lt;/p&gt;

&lt;p&gt;The whole thing is roughly &lt;strong&gt;6 KB gzipped&lt;/strong&gt;, with another 6 KB lazy chunk if you turn on the DevTools guard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two-line install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;watermark-shield
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;WatermarkShield&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;watermark-shield&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WatermarkShield&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a tiled watermark of the user's id, an active DevTools detector, and the always-on passive protections. Sensible defaults; nothing else to configure.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic production setup
&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;new&lt;/span&gt; &lt;span class="nc"&gt;WatermarkShield&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.5vw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                              &lt;span class="c1"&gt;// scales with screen&lt;/span&gt;
  &lt;span class="na"&gt;fontColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;     &lt;span class="c1"&gt;// light/dark aware&lt;/span&gt;
  &lt;span class="na"&gt;globalAlpha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                              &lt;span class="c1"&gt;// survives JPEG compression&lt;/span&gt;
  &lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;devtoolUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourapp.com/security/blocked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="na"&gt;onDevtoolOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;detectorType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// The user is about to be redirected. Fire and forget.&lt;/span&gt;
      &lt;span class="c1"&gt;// No userId in the body. The server resolves the user from&lt;/span&gt;
      &lt;span class="c1"&gt;// the session cookie or JWT, never from what the client sends.&lt;/span&gt;
      &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/audit/devtool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="nx"&gt;detectorType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;})],&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things worth pointing out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity belongs to the server.&lt;/strong&gt; The audit body has no &lt;code&gt;userId&lt;/code&gt;. The server resolves who the user is from the session cookie or the &lt;code&gt;Authorization&lt;/code&gt; header. If you trust the client to tell you who is logged in, an attacker can forge the audit log too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The audit log is the real backstop.&lt;/strong&gt; A determined attacker can defeat any browser-side defense (custom Chromium build, an extension that strips watermarks, JS overrides). But the beacon already left their browser before they got that far. The server log is what gives you attribution that survives sophisticated bypasses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always &lt;code&gt;devtool: false&lt;/code&gt; in development.&lt;/strong&gt; Otherwise your engineers cannot debug their own app. Gate it with &lt;code&gt;import.meta.env.PROD&lt;/code&gt; or &lt;code&gt;environment.production&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework bindings
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Angular&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;WatermarkShieldService&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;watermark-shield/angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shield&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// React&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;useWatermarkShield&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;watermark-shield/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;useWatermarkShield&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Vue 3&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;useWatermarkShield&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;watermark-shield/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;useWatermarkShield&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;devtool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;Same lifecycle in every binding: &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The goal is not to prevent capture. It is to make every captured image carry the leaker's name. That changes the calculation from "no one will know" to "of course I will be caught."&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;watermark-shield
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source and docs: &lt;a href="https://github.com/mhwazrah/watermark-shield" rel="noopener noreferrer"&gt;https://github.com/mhwazrah/watermark-shield&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>angular</category>
      <category>vue</category>
      <category>react</category>
    </item>
    <item>
      <title>Manage JSON Translations Easily in VS Code with JSON Translations Manager</title>
      <dc:creator>Mohammed Hassan </dc:creator>
      <pubDate>Mon, 03 Mar 2025 08:51:26 +0000</pubDate>
      <link>https://forem.com/matrix2526/manage-json-translations-easily-in-vs-code-with-json-translations-manager-56g2</link>
      <guid>https://forem.com/matrix2526/manage-json-translations-easily-in-vs-code-with-json-translations-manager-56g2</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;If you work with multi-language applications, managing JSON translation files can be a hassle. You often need to manually add, update, and verify translations across multiple JSON files. To make this process easier, I created the &lt;strong&gt;JSON Translations Manager&lt;/strong&gt; — a simple yet powerful VS Code extension to help you manage translation keys effortlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Mohammed.json-translations-manager" rel="noopener noreferrer"&gt;Download JSON Translations Manager&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. View All Translations at Once
&lt;/h3&gt;

&lt;p&gt;This extension shows all translation keys and values in a structured tree format, making reviewing missing or incorrect translations easy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Filplc8ft1n035dzsvl8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Filplc8ft1n035dzsvl8i.png" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Edit Translations Quickly
&lt;/h3&gt;

&lt;p&gt;You can directly edit translations within VS Code without switching between multiple files manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add Translations Quickly
&lt;/h3&gt;

&lt;p&gt;You can directly add the translation keys within VS Code, just write JTM in the text editor and all keys will show up as below image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pody8hwooyn1la6h0tq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pody8hwooyn1la6h0tq.png" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. View translation
&lt;/h3&gt;

&lt;p&gt;You can quickly view translation values within VS Code. Simply select a translation key and hover over it to see the corresponding values in all available languages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqmsxbllelf2sgatzpffz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqmsxbllelf2sgatzpffz.png" width="682" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Use
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Step 1: Install the Extension
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Open VS Code.&lt;/li&gt;
&lt;li&gt;  Go to &lt;strong&gt;Extensions&lt;/strong&gt; (Ctrl+Shift+X or Cmd+Shift+X on Mac).&lt;/li&gt;
&lt;li&gt;  Search for &lt;strong&gt;JSON Translations Manager&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  Click &lt;strong&gt;Install&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Set Up Your Localization Folder
&lt;/h3&gt;

&lt;p&gt;Before you start using JSON Translation Manager, you need to configure your localization (i18n) folder. Ensure it contains at least one JSON language file, such as en.json or ar.json. The recommended structure is:&lt;/p&gt;

&lt;p&gt;/locales/en.json&lt;br&gt;&lt;br&gt;
/locales/fr.json&lt;br&gt;&lt;br&gt;
/locales/de.json&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Initialize and Use JSON Translations Manager
&lt;/h3&gt;

&lt;p&gt;After setting up your localization folder, you need to initialize the extension to manage translations efficiently.&lt;/p&gt;

&lt;h4&gt;
  
  
  Available Commands
&lt;/h4&gt;

&lt;p&gt;You can access the following commands in VS Code’s Command Palette (&lt;strong&gt;Ctrl+Shift+P&lt;/strong&gt; or &lt;strong&gt;Cmd+Shift+P&lt;/strong&gt; on Mac):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;JTM: Initialize or Update JTM Configuration&lt;/strong&gt; — Set up or refresh the configuration for your translation files.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;JTM: Add Translation&lt;/strong&gt; — Add a new translation key using dot (.) notation for nested objects.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;JTM: Add Translation from Selected Text&lt;/strong&gt; — Convert selected text into a translation key automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once initialized, the extension scans your JSON files and presents them in a structured tree format.&lt;/p&gt;

&lt;h3&gt;
  
  
  JTM: Initialize or update JTM configuration
&lt;/h3&gt;

&lt;p&gt;This command will create or update the &lt;code&gt;jtmConfig.json&lt;/code&gt; file on your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hgj3iipdhjglufggzff.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hgj3iipdhjglufggzff.gif" width="480" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  JTM: Add translation
&lt;/h3&gt;

&lt;p&gt;This command will create a translation using the key entered and save it on your translation files on the project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You can use dot (.) notation to make the key a Nested&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ickgfufxe43w7t6ctir.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ickgfufxe43w7t6ctir.gif" width="480" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  JTM: Add translation from selected text
&lt;/h3&gt;

&lt;p&gt;This command will create a translation from the selected text and save it on your translation files on the project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You can use dot (.) notation to make the key a Nested&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflvc42b2nhja5we00r2h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflvc42b2nhja5we00r2h.gif" width="480" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Use JSON Translations Manager?
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Saves Time&lt;/strong&gt; No need to manually check multiple files.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Improves Workflow&lt;/strong&gt; Works directly inside VS Code.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Open Source&lt;/strong&gt; Free to use and improve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Managing translations in JSON files doesn’t have to be difficult. With &lt;strong&gt;JSON Translations Manager&lt;/strong&gt;, you can quickly review, edit, and organize your translations with ease.&lt;/p&gt;

&lt;p&gt;Try it now and improve your localization workflow!&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://marketplace.visualstudio.com/items?itemName=Mohammed.json-translations-manager" rel="noopener noreferrer"&gt;Install JSON Translations Manager&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have feedback or feature suggestions? Share your thoughts or contribute on &lt;a href="https://github.com/MohammedAldosari/json-translations-manager" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>frontend</category>
      <category>backend</category>
      <category>json</category>
    </item>
  </channel>
</rss>
