<?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: Mohamed Almearag</title>
    <description>The latest articles on DEV Community by Mohamed Almearag (@mohamed_almearag).</description>
    <link>https://dev.to/mohamed_almearag</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2285585%2F3ed4578d-7564-4028-b5e3-2af555be186a.jpeg</url>
      <title>DEV Community: Mohamed Almearag</title>
      <link>https://dev.to/mohamed_almearag</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohamed_almearag"/>
    <language>en</language>
    <item>
      <title>My pre-commit hook passed a broken commit</title>
      <dc:creator>Mohamed Almearag</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:51:09 +0000</pubDate>
      <link>https://dev.to/mohamed_almearag/my-pre-commit-hook-passed-a-broken-commit-1o2c</link>
      <guid>https://dev.to/mohamed_almearag/my-pre-commit-hook-passed-a-broken-commit-1o2c</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Mohamed-Almarag/locale-guard" rel="noopener noreferrer"&gt;Locale Guard&lt;/a&gt; is my GitHub Action and CLI for translation files. It catches missing keys, broken placeholders, plural forms and untranslated text on pull requests, and since version 1.3.0 (&lt;code&gt;npx locale-guard-cli&lt;/code&gt;) before each commit too. It's listed in the &lt;a href="https://www.i18next.com/overview/plugins-and-utils" rel="noopener noreferrer"&gt;i18next docs&lt;/a&gt; and the &lt;a href="https://github.com/oh-jon-paul/awesome-i18n" rel="noopener noreferrer"&gt;awesome-i18n&lt;/a&gt; list.&lt;/p&gt;

&lt;p&gt;The first version of its pre-commit hook let a broken commit through. Here's what went wrong and what I changed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz9m7n5tt2obp9rpqnzzw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz9m7n5tt2obp9rpqnzzw.gif" alt="locale-guard-cli in a terminal: it lists the issues in the changed files and stops a commit from a Husky pre-commit hook" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A pre-commit hook must check what the commit holds
&lt;/h2&gt;

&lt;p&gt;My first hook ran the CLI on the files on disk. Then I tried this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a key to &lt;code&gt;en.json&lt;/code&gt; and &lt;code&gt;git add&lt;/code&gt; it.&lt;/li&gt;
&lt;li&gt;Translate it in &lt;code&gt;ar.json&lt;/code&gt;, &lt;code&gt;de.json&lt;/code&gt; and &lt;code&gt;fr.json&lt;/code&gt;, but forget to &lt;code&gt;git add&lt;/code&gt; them.&lt;/li&gt;
&lt;li&gt;Commit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The hook passed. The files on disk were complete, so the check was happy. But the commit held only the English key, and the other languages were missing it.&lt;/p&gt;

&lt;p&gt;A pre-commit hook has to read the staged files, not the working tree. Now &lt;code&gt;--staged&lt;/code&gt; reads them from git: &lt;code&gt;git ls-files --stage&lt;/code&gt; lists each staged file with the id of its content, and one &lt;code&gt;git cat-file --batch&lt;/code&gt; process reads them all. With that, the same commit stops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;messages/ar.json
  2:3  error    Missing key Nav.deals  missing-keys

messages/de.json
  2:3  error    Missing key Nav.deals  missing-keys

messages/fr.json
  2:3  error    Missing key Nav.deals  missing-keys

✖ 3 new issues (3 errors) since the last commit
  2 existing issues are not shown. --all lists every issue.
husky - pre-commit script failed (code 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooks can be skipped with &lt;code&gt;--no-verify&lt;/code&gt;, so there is also a pre-push hook that compares the branch with &lt;code&gt;origin/main&lt;/code&gt;, and the check on the pull request stays on.&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;npx locale-guard-cli &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--files&lt;/span&gt; &lt;span class="s2"&gt;"locales/{locale}.json"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; vue-i18n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two demos you can clone and break, with &lt;a href="https://github.com/Mohamed-Almarag/locale-guard-demo-husky" rel="noopener noreferrer"&gt;Husky&lt;/a&gt; and &lt;a href="https://github.com/Mohamed-Almarag/locale-guard-demo-lefthook" rel="noopener noreferrer"&gt;Lefthook&lt;/a&gt;, and step-by-step &lt;a href="https://github.com/Mohamed-Almarag/locale-guard/discussions/categories/guides" rel="noopener noreferrer"&gt;guides&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If it reports something that isn't a bug, please &lt;a href="https://github.com/Mohamed-Almarag/locale-guard/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this post with help from an AI assistant, and checked every detail against the code and the test runs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>i18n</category>
    </item>
    <item>
      <title>The translation bugs code review keeps missing</title>
      <dc:creator>Mohamed Almearag</dc:creator>
      <pubDate>Sun, 18 Oct 2026 11:14:08 +0000</pubDate>
      <link>https://dev.to/mohamed_almearag/the-translation-bugs-code-review-keeps-missing-4d73</link>
      <guid>https://dev.to/mohamed_almearag/the-translation-bugs-code-review-keeps-missing-4d73</guid>
      <description>&lt;p&gt;In Dashy's Hindi translation, someone translated a variable name. The message said &lt;code&gt;{थीम}&lt;/code&gt; instead of &lt;code&gt;{theme}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The JSON was valid and the diff looked fine. But vue-i18n could not compile the message. In Hindi, clicking Save in the theme editor stored the colors and then threw an error, so no confirmation appeared and the editor stayed open.&lt;/p&gt;

&lt;p&gt;I found the same kind of bug in two other projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open WebUI:&lt;/strong&gt; placeholder names translated in 10 locales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;listmonk:&lt;/strong&gt; five Arabic messages lost their &lt;code&gt;{name}&lt;/code&gt;. Deleting a campaign said only "deleted", without the campaign name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three fixes were merged by the maintainers within a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why review misses them
&lt;/h2&gt;

&lt;p&gt;The reviewer usually can't read Hindi or Arabic. The file is valid JSON, so nothing fails. The bug shows up only when a user opens that screen in that language.&lt;/p&gt;

&lt;p&gt;Plural forms are worse. Arabic has six of them: zero, one, two, few, many and other. Most projects write two, because English has two.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/Mohamed-Almarag/locale-guard" rel="noopener noreferrer"&gt;Locale Guard&lt;/a&gt; is a GitHub Action that runs on every pull request. It reads the locale files with each library's own message parser, compares every language with the base one, and writes one comment with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing keys, with a block ready to paste into each file&lt;/li&gt;
&lt;li&gt;placeholders, ICU arguments and tags that differ from the base message&lt;/li&gt;
&lt;li&gt;messages that don't compile&lt;/li&gt;
&lt;li&gt;plural forms each language needs, from &lt;code&gt;Intl.PluralRules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;text that was never translated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works with vue-i18n (and Nuxt), next-intl, react-intl, i18next and Rails locale files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Old projects
&lt;/h2&gt;

&lt;p&gt;Most projects already have hundreds of these issues, so a check that fails on all of them gets turned off in a week.&lt;/p&gt;

&lt;p&gt;Locale Guard runs the same checks on the base branch and fails only on what the pull request adds. Old issues are counted and collapsed. On Directus, with 70 languages and about 104,000 existing issues, a pull request is still judged only on what it changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Translations&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;locale-guard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mohamed-Almarag/locale-guard@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;locales/{locale}.json&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vue-i18n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try it and it reports something that isn't a bug, please &lt;a href="https://github.com/Mohamed-Almarag/locale-guard/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;. False positives are the first thing I want to fix.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this post with help from an AI assistant, and checked every detail against the merged pull requests.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>i18n</category>
      <category>githubactions</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
