<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TAIYAKU WORKS Notes]]></title><description><![CDATA[TAIYAKU WORKS Notes]]></description><link>https://taiyakuworks.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>TAIYAKU WORKS Notes</title><link>https://taiyakuworks.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 12 Oct 2026 11:05:12 GMT</lastBuildDate><atom:link href="https://taiyakuworks.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Matching JSON Keys Can Still Hide Reversed Choices]]></title><description><![CDATA[Two localization files can have matching keys, matching types, and no placeholder errors while offering the wrong action to a player.
Consider this English source:
{"choices":["Yes","No"]}

And this J]]></description><link>https://taiyakuworks.hashnode.dev/matching-json-keys-can-still-hide-reversed-choices</link><guid isPermaLink="true">https://taiyakuworks.hashnode.dev/matching-json-keys-can-still-hide-reversed-choices</guid><category><![CDATA[localization]]></category><category><![CDATA[json]]></category><dc:creator><![CDATA[TAIYAKU WORKS]]></dc:creator><pubDate>Sun, 27 Sep 2026 04:09:14 GMT</pubDate><content:encoded><![CDATA[<p>Two localization files can have matching keys, matching types, and no placeholder errors while offering the wrong action to a player.</p>
<p>Consider this English source:</p>
<pre><code class="language-json">{"choices":["Yes","No"]}
</code></pre>
<p>And this Japanese target:</p>
<pre><code class="language-json">{"choices":["いいえ","はい"]}
</code></pre>
<p>The target means “No, Yes.” Both arrays contain two strings. A structural comparison that pairs elements by index has nothing structural to flag. If the application expects the first item to represent an affirmative choice, the translation now describes the opposite action.</p>
<p>That is a useful test case for any localization review process: include a known bad translation that your structural checker is expected to miss.</p>
<h2>Decide what identifies a string</h2>
<p>JSON objects and arrays give a checker different ways to match content. An object uses member names; an array uses positions. <a href="https://www.rfc-editor.org/rfc/rfc8259">RFC 8259</a> describes objects as unordered collections and arrays as ordered sequences. A formatter's choice of indentation is separate from either relationship.</p>
<p>These objects should therefore be compared by <code>play</code> and <code>exit</code>, even though their order differs:</p>
<pre><code class="language-json">{"menu":{"play":"Play","exit":"Exit"}}
</code></pre>
<pre><code class="language-json">{
  "menu": {
    "exit": "終了",
    "play": "開始"
  }
}
</code></pre>
<p>A text diff shows many changed lines. A key-based localization check can concentrate on the two corresponding strings.</p>
<p>Arrays need extra care. Remove the middle item from a three-step list:</p>
<pre><code class="language-json">{"steps":["Open","Save","Close"]}
</code></pre>
<pre><code class="language-json">{"steps":["開く","閉じる"]}
</code></pre>
<p>An index-based comparison pairs <code>Save</code> with <code>閉じる</code> (“Close”) at index 1 and reports a missing index 2. It can flag the length difference and missing position. It cannot infer that the translator removed the middle step, then realign the remaining elements by meaning.</p>
<p>If your application's format allows stable identifiers, names such as <code>save</code> and <code>close</code> can make those relationships easier to inspect. Changing an established file schema also requires changing its consumer; renaming the keys in a translation file alone is not a migration.</p>
<h2>Eight small tests against a released checker</h2>
<p>The following results came from Taiyaku JSON Check 1.1.1, a tool published by TAIYAKU WORKS. They describe this implementation, rather than every JSON comparison tool.</p>
<p>For this check, the engine embedded in each distributed HTML file was run with Node.js 24.19.0. The HTML hashes matched the release manifest. Both the free and full editions produced the same results for all eight pairs. Each input went through the product's parser before comparison.</p>
<p>Unchanged-string notices were disabled except in the dedicated unchanged case. The placeholder case used <strong>Simple placeholders</strong> mode. These are small synthetic checks, not a translation-quality benchmark or application runtime tests.</p>
<table>
<thead>
<tr>
<th>Change</th>
<th>Observed result</th>
</tr>
</thead>
<tbody><tr>
<td>Reorder <code>play</code> and <code>exit</code>, change indentation, translate their values</td>
<td>No findings; two strings compared</td>
</tr>
<tr>
<td>Replace the key <code>quit</code> with <code>leave</code></td>
<td><code>MISSING /menu/quit</code> error and <code>EXTRA /menu/leave</code> warning</td>
</tr>
<tr>
<td>Change <code>count: 3</code> to <code>count: "3"</code>, and <code>enabled: true</code> to <code>enabled: false</code></td>
<td><code>TYPE /count</code> error and <code>VALUE /enabled</code> warning</td>
</tr>
<tr>
<td>Shorten the three-step array above to two elements</td>
<td><code>ARRAY /steps</code> warning and <code>MISSING /steps/2</code> error</td>
</tr>
<tr>
<td>Translate <code>Yes, No</code> as <code>No, Yes</code>, keeping two array elements</td>
<td>No findings; two strings compared</td>
</tr>
<tr>
<td>Translate <code>{"door":"Open"}</code> as <code>{"door":"閉じる"}</code> (“Close”)</td>
<td>No findings; one string compared</td>
</tr>
<tr>
<td>Leave <code>{"button":"Settings"}</code> unchanged</td>
<td><code>UNCHANGED /button</code> informational finding</td>
</tr>
<tr>
<td>Translate <code>{"notice":"Found %s items"}</code> as <code>{"notice":"%d個見つかりました"}</code></td>
<td><code>TOKEN /notice</code> error in Simple placeholders mode</td>
</tr>
</tbody></table>
<p>The paths identify locations in the file. The severity describes the checker's review priority. Neither is a verdict on the quality of the translation.</p>
<p>An extra target key can be intentional. A proper name can correctly remain unchanged. A boolean change may be significant to the application even though this checker labels it a warning. Decide which findings block a release using the requirements of the application that consumes the files.</p>
<h2>Inspect the input before comparing it</h2>
<p>A duplicate key creates an earlier problem:</p>
<pre><code class="language-json">{"label":"A","label":"B"}
</code></pre>
<p>The tested parser rejected this input with <code>Duplicate object key: /label</code>. That is an explicit parser policy. RFC 8259 recommends unique member names and explains that implementations differ when names repeat. If parsing silently discards one occurrence, a later comparison cannot inspect the value that was lost.</p>
<p>For a localization pipeline, record parse failures separately from comparison findings. “Could not compare this file” must not become “zero issues.”</p>
<h2>Keep the structural and language reviews connected</h2>
<p>A useful handoff has two parts:</p>
<ol>
<li><p>A structural review queue: missing keys, type changes, changed non-text values, array differences, and supported placeholder checks.</p>
</li>
<li><p>A contextual review: what each string means, which action it represents, how variables are supplied, and how it appears in the application.</p>
</li>
</ol>
<p>For arrays, include one case where an item is removed and one where equal-length items are reversed. For strings, include an opposite-meaning translation with the same key and type. These cases expose the boundary of the checker before a clean result acquires more authority than it deserves.</p>
<p>You can reproduce the pairs above in the <a href="https://taiyaku-tools.itch.io/taiyaku-json-check-browser-trial">free browser trial</a>. Save each source and target as separate UTF-8 JSON files, load one pair at a time, and use the settings described above. The trial allows one pair and up to 100 strings, without report export. Use these synthetic examples or anonymized files; the hosting page is online. An offline edition is linked from the trial page.</p>
<p>A clean result means the enabled rules found no issue in their supported scope. Keep the reversed-choice example beside that result when deciding what to check next.</p>
]]></content:encoded></item></channel></rss>