ファイル形式・標準規格Read this article in English

YAML対JSON — 翻訳ファイルとしてのネスト、コメント、バージョン管理での挙動

多くのソフトウェアプロジェクト — 特にWebフレームワークやJavaScript寄りのスタック — は、プラットフォーム固有のリソース形式ではなくYAMLやJSONで翻訳を保存します。どちらも同じデータ(ネストしたキー、言語ごとの1ファイル)を十分に表現できます。しかし実際には交換可能ではありません。違いが表れるのは「何を保存できるか」ではなく、「人間が編集し、バージョン管理システムが2人分の変更をマージしようとした時にどう振る舞うか」だからです。

ネストとキーパス

どちらの形式もネストしたオブジェクトをサポートし、翻訳ツールはほぼ常にこのネストを使って画面や機能単位でキーをグループ化し、nav.settings.titleのようなドット区切りのキーパスで特定の文字列を指定します。

{
  "nav": {
    "settings": { "title": "Settings" }
  },
  "errors": {
    "network": "Could not connect. Check your connection and try again."
  }
}

同じ構造をYAMLで書くと、波括弧やキー全体のクォートの代わりにインデントに頼ります。

nav:
  settings:
    title: Settings
errors:
  network: Could not connect. Check your connection and try again.

コメント — 実質的な構造上の違い

JSONにはコメント構文が一切なく、ファイル自体の中に翻訳者向けのメモを残す標準的な方法がありません。チームは_commentのような接尾辞を付けた隣接キーや、別のメタデータファイルといった回避策で対応しますが、それはあくまで回避策であって、形式そのものの機能ではありません。

YAMLはハッシュ記号によるコメントをネイティブにサポートしており、翻訳者向けの文脈を、それが説明する文字列のすぐ隣に置くことができます。これは.stringsや.propertiesファイルにおけるコメントと同じ役割です。

errors:
  # Shown when a network request times out; keep it short, fits a small toast
  network: Could not connect. Check your connection and try again.

複数行文字列とインデントの落とし穴

YAMLには複数行テキスト専用の構文があります。|で始まるブロックスカラー(改行を保持)や>(改行をスペースに畳み込む)は、1行に詰め込まれたエスケープ済み\nだらけのJSON文字列よりも、長い文章にとってはるかに自然に読めます。これは、利用規約のテキストやゲーム内の長い会話のような、文章量の多いコンテンツに関して、YAMLがJSONに対して持つ明確な使い勝手上の利点の一つです。

その読みやすさの代償として、YAMLの構造は空白によって定義されており、インデントのミスは起こしやすく、しかもしばしば無警告です。タブとスペースを混在させたり、1行を1桁ずらしたりすると、パースエラーを一切出さないまま、その値がどのキーに属するかが変わってしまうことがあります — ファイルは依然として有効なYAMLのままですが、作者が意図した構造とは違うものになっています。JSONは逆の問題を持ちます。構造が明示的でインデントに依存しないため、括弧の位置ミスやカンマの欠落は大きく即座に失敗します。これは手編集する人間にとっては安全側に働きますが、まさにそれゆえにJSONにはコメントも寛容な複数行構文もありません — この形式は表現力とその厳格さを引き換えにしています。

アンカー、末尾カンマ、キーの重複

YAMLはアンカーとエイリアス(&であるコンテンツブロックに一度名前を付け、*で別の場所から再利用する)をサポートしており、本当に同じ意味で二度現れる文字列の繰り返しを避けられますが、翻訳ファイルにおいてはこれは便利さより危険の方が大きい機能です。翻訳者には、ある値がエイリアスなのかリテラルなのかを確実に知る方法がなく、ある言語では2つの文脈で本当は違う言い回しが必要なのに同じアンカーを再利用してしまうと、ファイルを見ているだけでは誰も気づかないまま誤訳が静かに生成されます。

JSONは末尾カンマを許さない設計になっているため、オブジェクトや配列の最後の要素の後の余分なカンマは、警告ではなく明確なパースエラーになります。YAMLのツール群は末尾の小さな空白には比較的寛容ですが、インデントには厳格です。そのため、大きく失敗するミスの種類は、どちらも根底では空白に隣接した構文でありながら、両者で異なります。

最も鋭い違いはキーの重複です。JSON仕様もYAML仕様も、パーサに重複キーの拒否を義務付けていません。実際には両形式のほとんどのパーサが重複を静かに受け入れ、1つの値だけ(通常は最後に出現したもの)を残します。翻訳者がブロックをコピー&ペーストしてキー名の変更を忘れると、形式そのものからは何の警告もないまま既存の翻訳を上書きしてしまいます。これを検知するにはリンターやスキーマチェックを上に重ねる必要があり、どちらの形式もそれをタダで保証してはくれません。

バージョン管理でのマージコンフリクトの挙動

どちらの形式も行ベースのテキストなので、標準的な行差分マージツールで扱えますが、コンフリクト時の挙動は同一ではありません。JSONは括弧とカンマが明示的であるため、片方のブランチではきれいに解決した自動3-wayマージが、もう片方に末尾カンマの残留や括弧の不整合を残す、というよくある厄介な失敗モードがあります — ファイルはマージされたように見えて、実はパースに失敗する状態になっていることがあります。

YAMLは残留すべきカンマがそもそも存在しないため、この種のコンフリクトを完全に回避しますが、2つのブランチの行がわずかに異なるインデントレベルで交互に組み合わさるマージは、コンフリクトマーカーを一切出さないまま構造的に異なるツリーを静かに生み出すことがあります(あるキーを誤った親の下に付け替えてしまうなど)。マージツールには先頭の空白量の違いしか見えず、テキスト上のコンフリクトとして検出されないからです。どちらの形式も、自動マージ後に盲目的に信頼して良いものではありません。JSONの差分は少なくとも目に見える形で失敗する傾向がありますが、悪いYAMLのマージは検証を通過してしまいながら間違っていることがあります。

翻訳者が直接ファイルを編集する場合の意味

翻訳者が専用ツールを介さず生のファイルで作業する場合、JSONの厳格さは正味の安全機能になります — 構文ミスは出荷される前に即座に検出されます。一方でYAMLのコメントと複数行ブロックは、文脈を書き残したり長い文章を書いたりする人にとって実質的な使い勝手の向上です。どちらが客観的に優れているということはなく、多くのプロジェクトにとって実務的な答えは「翻訳が実際にどう行われるか」で決まります。エディタで人間が手編集するならインデントのリスクを差し引いてもYAMLのコメントは価値があり、ファイルをシリアライズするツールを通してのみ編集されるならJSONの厳格さとシンプルな差分の方が通常はより安全な既定値です。

関連記事