<?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 Almarag</title>
    <description>The latest articles on DEV Community by Mohamed Almarag (@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%2Fde642953-d4c4-45ac-80d0-f08c4a25c2b2.jpg</url>
      <title>DEV Community: Mohamed Almarag</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>The translation bugs code review keeps missing</title>
      <dc:creator>Mohamed Almarag</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>
