Engineeringこの記事を日本語で読む

CSV vs JSON for localization files: what actually matters

CSV and JSON both show up constantly as the interchange format for localization text, and teams sometimes pick between them on habit rather than fit. Each has real strengths and real failure modes. This is a practical comparison, plus the properties that matter more than the format choice itself, since the format is rarely the reason a pipeline breaks — the assumptions built around it usually are.

CSV: spreadsheet-friendly, quoting-hostile

CSV's main appeal is that every translator already knows how to open, filter, and comment in a spreadsheet — no tooling to install. Its main risk is the quoting rules that most people never read carefully. The CSV spec says a field containing a comma, a double quote, or a newline must be wrapped in double quotes, and any double quote inside that field must be doubled. Most breakage traces back to a translator or a spreadsheet export not following that rule.

  • A comma inside an unquoted field silently shifts every following column
  • A stray unescaped double quote inside a quoted field ends the field early
  • Multiline cells (dialogue with an embedded line break) are legal CSV but many hand-rolled parsers don't expect them and split the row
  • Spreadsheet software exports encoding is not guaranteed — some export legacy encodings by default depending on system locale, which corrupts non-ASCII text silently
key,en,ja
shop.buyButton,Buy,購入
shop.buyConfirmSentence,"Buy this item, right now?",本当に購入しますか?

JSON: structured, tooling-friendly, translator-hostile raw

JSON's strength is structure: nesting mirrors your key namespace directly, and every language runtime already has a JSON parser, so there's no custom parsing code to get wrong. It's a strict format — trailing commas and comments are not valid JSON — which is exactly the property that makes it good for machines and bad for people, since a single stray character can make the whole file unparseable rather than just one row wrong.

  • No comments field, so context for a translator has to live somewhere else (a separate key or a separate file)
  • Two people editing the same nested object in parallel produce merge conflicts that are painful to resolve by hand, since JSON has no line-level granularity the way CSV rows do
  • Handing a translator a raw JSON file invites a broken brace or a missing comma that silently fails validation, or worse, fails to fail and just doesn't parse where the game reads it
{
  "shop": {
    "buyButton": "Buy",
    "buyConfirmSentence": "Buy this item, right now?"
  }
}

What actually matters more than the format

Neither format's weaknesses are fatal on their own — they're manageable if the pipeline around the file is designed with a few properties in mind. These properties matter regardless of which format ends up in the repository, and a team that gets them right can tolerate either format's rough edges.

  • Single source of truth: one file (or one generated export) is authoritative; nobody hand-edits a copy that later has to be reconciled
  • Diff-ability: a small text change should produce a small, readable diff — a re-serialized JSON file with reordered keys or re-indented CSV defeats this even when the actual content barely changed
  • Round-trip safety: importing a file and re-exporting it should be byte-for-byte stable except for the intended change, so review tools and version control show real edits, not formatting noise
  • Encoding declared, not assumed: every file states or is verified to be UTF-8 before it enters the pipeline, regardless of which format carries it

A workable split

A common, practical pattern is to keep the authoritative source in a structured format (JSON, or a database) that tooling can validate, and generate a CSV export specifically for the translation step — then re-import and re-validate before merging. That way translators get the spreadsheet workflow they're used to, without the game's data model ever depending on a hand-edited CSV surviving a round trip through someone's spreadsheet application untouched.

Related articles