QA & troubleshootingこの記事を日本語で読む

Reading a diff of your localization files between builds

A diff — the line-by-line comparison a version control system shows between two versions of a file — is one of the cheapest tools you have for understanding what happened to a localization file between two builds. It costs nothing to run and, when the file cooperates, it tells you exactly which strings moved. The problem is that localization files do not always cooperate.

A file that produces a wall of red and green for a one-line content change is not telling you anything useful anymore. Getting real value out of a diff takes a bit of discipline in how the file is written and read, not just running the comparison.

Why a reformat wrecks a diff

Most diff tools compare text line by line, with no understanding of the file's structure. If a JSON export changes its indentation from two spaces to four, or a CSV export starts quoting fields it previously left bare, every line in the file looks different even though not a single translation changed. The diff is technically correct — the bytes did change — but it is useless for its actual purpose, which is telling a human what to review.

The same happens with key reordering. Sorting keys alphabetically, or regenerating a file from a database in a different row order, moves every entry to a new line position. A line-based diff sees that as every line being deleted and re-added, even for entries whose content is byte-for-byte identical.

// before
{
  "menu.play": "Play",
  "menu.settings": "Settings"
}

// after — only "menu.settings" changed, but a naive
// alphabetical re-sort plus a formatting change makes
// the diff show both lines as fully rewritten
{
    "menu.play": "Play",
    "menu.settings": "Options"
}

Keeping diffs meaningful

The fix is not a smarter diff tool — it is discipline about what the export step is allowed to change. If keys are always written in the same order (source key order, or a fixed alphabetical order, chosen once and never switched), and the serializer always uses the same indentation and quoting rules, a diff between two exports reflects only actual content changes. This matters more than it looks, because the export step is often automated and easy to tweak without anyone treating it as a change to the file's contract.

For CSV specifically, this means fixing column order, fixing whether every field gets quoted or only fields that need it (RFC 4180 allows either, consistently), and fixing line-ending style. For JSON, it means a stable key order and a stable indent width. None of this is about correctness — a reformatted file and the original both parse to the same data — it is entirely about keeping the diff readable by a person.

Telling a source change from a target change

A translation file typically carries both the source text (for context) and the target text (the actual translation) side by side. A diff on that file can mean two very different things: the source text changed, which means the translation is now stale and needs re-translation, or only the target text changed, which means someone updated the translation and it needs review.

If your file format or diff view does not make this distinction obvious, every reviewer has to reconstruct it by eye, which is slow and error-prone at scale. Separating these two cases — for example, by diffing the source column and the target column independently, or by having your tooling tag each changed row with which side changed — turns one long list of red-and-green lines into two much shorter, purpose-specific lists.

  • Source changed, target unchanged: translation is now out of date, needs re-translation
  • Source unchanged, target changed: translation was edited, needs review
  • Both changed: likely a re-translation after a source update, review both
  • Neither changed but the line moved: formatting or ordering noise, not a real change

Using the diff to scope review, not to replace it

The whole point of a clean diff is that it tells you what needs a fresh look and, just as importantly, what does not. A translation file with a thousand entries where a build only changed twelve of them should not require re-reviewing all thousand — the diff is the evidence that the other 988 are unchanged since the last time they were reviewed and approved.

This only holds if the diff is trustworthy, which loops back to the formatting discipline above. A team that treats a noisy diff as normal ends up either reviewing everything every time — which does not scale — or reviewing nothing and hoping — which is worse. A clean diff is what makes 'review only what changed' a safe policy instead of a guess.

Related articles