目次
JSON翻訳ファイル:i18nのフォーマット、構造、ベストプラクティス
複数言語でリリースするほぼすべてのWebアプリケーションは、翻訳をJSONファイルに保存します。このフォーマットは広く普及していますが、その規約は統一されていません。フレームワークごとに異なる形式が期待されます。フラットなキーと値のオブジェクト、深くネストされた名前空間ツリー、ICU構文を埋め込んだメッセージ、または専用のデスクリプター構造を持つChrome拡張機能マニフェストなど、様々な形式があります。早い段階で誤ったフォーマットを選択したり、後からフォーマット間を移行したりすると、いずれも実際のコストが発生します。
このガイドでは、現在使用されている主要なJSON翻訳フォーマット、大規模での翻訳ファイルの整理方法、キー命名規則、複数形パターン、バリデーションのためのツール、そしてJSONとXLIFFやPOなどの代替フォーマット間の変換方法について説明します。
TL;DR / 主要なポイント
- JSONはWebのi18nで主流のフォーマットです。JavaScriptにネイティブで、人間が読みやすく、バージョン管理との相性も良好です。ただし、「JSONのi18nフォーマット」は一つではありません。主要なライブラリはそれぞれ独自の規約を定義しています。
- 最も一般的なバリアントは、フラットなキーと値(シンプルだが大規模では管理困難)、ネストされた/名前空間付き(整理されているが冗長)、i18next(名前空間付きで組み込みの複数形キーサフィックスあり)、FormatJS/react-intl(値にICU MessageFormat)、そしてchrome.i18n(
messageフィールドとオプションのdescriptionを持つデスクリプターオブジェクト)です。 - ファイルの整理はフォーマットと同様に重要です。翻訳を名前空間や機能ドメインで分割することで、ファイルを小さく保ち、マージコンフリクトを減らし、遅延読み込みを可能にします。
- 複数形と変数補間の構文はライブラリによって異なります。値にICU MessageFormatを使用すると、最も移植性が高く表現豊かな選択肢が得られますが、互換性のあるランタイムが必要です。
- CIバリデーション(キーの欠落、不正なJSON、未翻訳文字列のチェック)は、壊れた翻訳が本番環境に到達するのを防ぐ最も効果的な方法です。
なぜ翻訳にJSONを使うのか?
フォーマットを比較する前に、JSONがWebのi18nのデフォルトになった理由と、その選択が常に明確ではない理由を理解する価値があります。
人間が読みやすくdiffに適している。 JSON翻訳ファイルはツールなしで読めます。プルリクエストのdiffは、どの文字列が変更され、追加され、削除されたかを正確に示します。XLIFF 2.0では同じ変更が複数の要素のXML属性に埋もれ、コンパイル済みバイナリフォーマットではdiffが読めなくなるのと比べてみてください。
JavaScriptにネイティブ。 JSON.parse()はすべてのJavaScriptランタイムに組み込まれています。インストールすべき解析依存関係も、設定すべきフォーマットアダプターもありません。Next.jsアプリケーションがブラウザ向けに翻訳をバンドルする際、プレーンなJSONを出荷します。シリアライゼーションのオーバーヘッドも、カスタムパーサーもありません。
プログラムによる生成と消費が容易。 すべての言語にJSONライブラリがあります。データベース、スプレッドシートのエクスポート、または翻訳管理システムAPIから翻訳ファイルを生成するのは、単純なJSONシリアライゼーションタスクです。
バージョン管理との相性が良い。 JSONファイルはテキストです。異なるキーに変更がある場合はクリーンにマージされ、読みやすいblame履歴を生成し、標準的なdiffツールで動作します。
代替手段とそのトレードオフ
| フォーマット | 強み | 弱み |
|---|---|---|
| XLIFF 2.0 | リッチなメタデータ、翻訳者ノート、状態追跡、業界標準 | 冗長なXML、diffの可読性が低い、複雑なツール |
| PO / Gettext | 成熟したエコシステム、msgid/msgstrモデルがコンテキストをサポート、複数形が組み込み | 専用パーサーが必要、JavaScriptではあまり一般的でない |
| YAML | 読みやすい、複数行文字列を自然にサポート | インデント依存(エラーが起きやすい)、ネイティブブラウザパーサーなし |
| ARB (Application Resource Bundle) | Flutter/Dartにネイティブ、キーごとのメタデータをサポート | Dart固有のエコシステム、Webツールが限定的 |
| JSON | 広く普及、JavaScriptにネイティブ、優れたツール | 組み込みメタデータなし、複数形の処理はライブラリによって異なる |
XLIFFは翻訳がCATツールを使用する専門翻訳会社を通じて流れる場合に適しています。POはGettextエコシステム(Poedit、GNU gettextユーティリティ)を活用したいプロジェクトに適しています。React、Vue、Angular、またはSvelteで構築されたほとんどのWebアプリケーションでは、JSONが実用的なデフォルトです。
一般的なJSON翻訳フォーマット
フラットなキーと値
最もシンプルなJSON翻訳ファイルは、すべてのキーが文字列識別子で、すべての値が翻訳された文字列である単一レベルのオブジェクトです。
{
"welcome_message": "Welcome to our platform",
"login_button": "Log in",
"logout_button": "Log out",
"error_not_found": "Page not found",
"error_server": "Something went wrong. Please try again."
}
このフォーマットは学習曲線がなく、任意のJSONパーサーで動作します。問題はスケール時に現れます。大規模アプリケーションで500のキーを持つフラットなファイルはナビゲートが不可能になります。視覚的なグルーピングがなく、文字列がどこで使用されているかの表示もなく、遅延読み込みのために翻訳のサブセットを抽出する方法もありません。
最適な用途: 小規模プロジェクト、プロトタイプ、または翻訳キーが100未満のアプリケーション。
ネストされた/名前空間付きJSON
ネストされたJSONは、アプリケーションの構造を反映した階層にキーを整理します。
{
"auth": {
"login": {
"title": "Sign in to your account",
"email_label": "Email address",
"password_label": "Password",
"submit_button": "Sign in",
"forgot_password": "Forgot your password?"
},
"logout": {
"confirm": "Are you sure you want to sign out?"
}
},
"dashboard": {
"greeting": "Good morning, {{name}}",
"stats": {
"total_users": "Total users",
"active_sessions": "Active sessions"
}
}
}
vue-i18nやangular/localizeなどのライブラリはネストされたJSONをネイティブにサポートしています。キーはドット記法でアクセスされます:t('auth.login.title')。この構造はコンポーネント階層に自然にマッピングされ、各文字列がどこで使用されているかを特定しやすくなります。
トレードオフは、深くネストされたオブジェクトがより慎重なキー管理を必要とし、長いアクセスパスが生成される可能性があることです。ほとんどのチームは実用的な最大値として2〜3レベルのネストに落ち着きます。
i18nextフォーマット
i18nextはJavaScriptエコシステムで最も広く使用されているi18nライブラリで、ネストされたJSONの上に独自の規約があります。最も重要なのは複数形キーサフィックスシステムです。
{
"common": {
"save": "Save",
"cancel": "Cancel",
"loading": "Loading..."
},
"items": {
"count_one": "{{count}} item",
"count_other": "{{count}} items",
"count_zero": "No items"
},
"notifications": {
"new_one": "You have {{count}} new notification",
"new_other": "You have {{count}} new notifications"
}
}
i18nextはベースキーにサフィックスを付加することで複数形を解決します。サフィックスはCLDRの複数形ルールカテゴリに従います:zero、one、two、few、many、other。英語ではoneとotherのみが適用されます。アラビア語やロシア語では、すべてのセットが重要になります。
i18nextはファイルレベルでの名前空間もサポートしています。各JSONファイルが名前空間です。t('common:save')を呼び出すと、i18nextはアクティブなロケールのcommon.jsonを読み込みます。これによりreact-i18nextを使用するReactアプリケーションで名前空間レベルの遅延読み込みが可能になります。
{
"title": "Welcome, {{name}}!",
"description": "You joined on {{date, datetime}}",
"price": "{{price, currency}}"
}
{{value, formatType}}構文はi18nextのフォーマットパイプラインをトリガーし、日付、数値、通貨のIntl APIと統合します。
FormatJS / react-intlフォーマット
FormatJS(react-intlを駆動する)は、ICU MessageFormat構文を文字列値に直接埋め込みます。これにより、すべての複数形と変数のロジックがキーサフィックスではなくメッセージ文字列自体の中に置かれます。
{
"welcome": "Welcome, {name}!",
"item_count": "{count, plural, =0 {No items} one {# item} other {# items}}",
"last_login": "Last login: {date, date, medium}",
"account_status": "{gender, select, male {He is} female {She is} other {They are}} a verified member.",
"notification_badge": "{count, plural, =0 {} one {({count})} other {({count})}}"
}
ICU MessageFormatは、複雑な複数形と性の一致ルールに最も表現豊かな選択肢です。selectキーワードは条件付き文字列を処理し、pluralはカウントベースの形式を処理し、フォーマットタイプ(date、number、currency)はIntl APIに委任します。
キーは通常、コンポーネントパスや意味的な記述子に一致するフラットな文字列です。FormatJSは深くネストされた構造よりも記述的なフラットキーを推奨します。
react-intlでの使用:
import { useIntl, FormattedMessage } from 'react-intl';
function ItemList({ count }) {
const intl = useIntl();
return (
<p>
<FormattedMessage id="item_count" values={{ count }} />
</p>
);
}
chrome.i18nフォーマット
Chrome拡張機能はchrome.i18n APIで定義された特定のJSONフォーマットを使用します。各キーはプレーンな文字列ではなくデスクリプターオブジェクトにマッピングされます。
{
"extensionName": {
"message": "My Extension",
"description": "The name of the extension, displayed in the Chrome Web Store."
},
"welcomeMessage": {
"message": "Hello, $USER$!",
"description": "Greeting shown when the extension opens",
"placeholders": {
"user": {
"content": "$1",
"example": "Alice"
}
}
},
"itemCount": {
"message": "$COUNT$ items selected",
"placeholders": {
"count": {
"content": "$1",
"example": "3"
}
}
}
}
descriptionフィールドはエンドユーザーには表示されません。翻訳者とChrome Web Storeのレビュープロセス向けのコンテキストです。プレースホルダーは$PLACEHOLDER_NAME$規約を使用し、コンテンツと例の値を定義する別のplaceholdersオブジェクトがあります。
このフォーマットはChrome拡張機能では交渉の余地がありません。一般的なWebアプリケーションのi18nには適していません。
ファイル整理パターン
ロケールごとの単一ファイル
最もシンプルな整理方法は、アプリケーションのすべての翻訳を含むロケールごとの1つのJSONファイルです。
locales/ en.json fr.json de.json ja.json es.json
小規模アプリケーションでは機能しますが、スケール時に問題が生じます。ファイルが大きくなり、無関係な機能が同じファイルでマージコンフリクトを引き起こし、ユーザーが訪問していないセクションの翻訳を遅延読み込みできません。
名前空間による分割
中規模および大規模アプリケーションで最も一般的なパターンは、名前空間による翻訳の分割です。機能領域ごとにロケールごとに1つのJSONファイルを作成します。
locales/
en/
common.json
auth.json
dashboard.json
settings.json
errors.json
fr/
common.json
auth.json
dashboard.json
settings.json
errors.json
de/
common.json
auth.json
dashboard.json
settings.json
errors.json
各名前空間ファイルは小さく、焦点が絞られています。common名前空間には、アプリケーション全体で使用される文字列(ボタンラベル、フォームフィールドラベル、汎用エラーメッセージ)が含まれます。機能名前空間には、その機能に固有の文字列が含まれます。
i18nextとreact-i18nextはこのパターンをネイティブに使用します。t('auth:login.title')はアクティブなロケールのauth.jsonを読み込みます。next-i18nextを使用するNext.jsアプリケーションはこの構造を規約として使用します。
機能ベースの分割
非常に大規模なアプリケーション、または各パッケージが独自の翻訳を所有するモノレポの場合、機能ベースの構造は翻訳ファイルをそれを使用するコードの隣に配置します。
src/
features/
auth/
locales/
en.json
fr.json
components/
LoginForm.tsx
SignupForm.tsx
dashboard/
locales/
en.json
fr.json
components/
DashboardHeader.tsx
checkout/
locales/
en.json
fr.json
このパターンは凝集性を最大化します。翻訳ファイルはそれを使用するコンポーネントの隣に存在します。ビルドシステムまたはi18nローダーが名前空間ファイルをランタイムまたはビルド時にマージします。欠点は、特定のロケールのすべての翻訳を見つけるためにソースツリー全体を横断する必要があり、翻訳者と翻訳管理システムのワークフローが複雑になることです。
推奨: ほとんどのアプリケーションには名前空間による分割を使用してください。チームが十分に大きく、異なるスクワッドが異なる機能を所有し、翻訳ファイルでのクロス機能のマージコンフリクトを避ける必要がある場合にのみ、機能ベースの分割を使用してください。
翻訳ファイルフォーマット比較表
| フォーマット | 可読性 | ツールサポート | メタデータサポート | ICUサポート | Diffの親しみやすさ |
|---|---|---|---|---|---|
| JSON(フラット) | 高 | 優秀 | なし | なし(デフォルト) | 優秀 |
| JSON(ネスト) | 高 | 優秀 | なし | なし(デフォルト) | 優秀 |
| JSON(FormatJS) | 中 | 良好 | なし | あり(ネイティブ) | 良好 |
| XLIFF 2.0 | 低 | 優秀(CATツール) | 豊富(状態、ノート、代替翻訳) | あり | 不良 |
| PO / Gettext | 中 | 良好(Poedit、Weblate) | 中(コメント、コンテキスト) | 部分的 | 中 |
| YAML | 高 | 中 | なし | なし | 良好 |
| ARB | 高 | 良好(Flutter) | キーごとのメタデータ | なし | 良好 |
XLIFFのメタデータの優位性は、専門翻訳会社と連携する場合に重要です。このフォーマットは翻訳状態(新規、翻訳済み、レビュー済み、最終)を追跡し、代替翻訳をサポートし、Phrase、memoQ、SDL TradosなどのほとんどのプロフェッショナルCATツールのネイティブフォーマットです。翻訳ワークフローに代理店への引き渡しが含まれる場合、生のJSONを送るのではなくJSONソースからXLIFFを生成することで、翻訳者の生産性が向上し、エラーが減少します。
キー命名規則
一貫したキー命名は、保守しやすい翻訳ファイルとバグや混乱の元になるファイルの違いです。
ドット記法とネスト
ドット付きのフラットキーと真にネストされたオブジェクトはどちらも階層を表しますが、動作が異なります。ドット付きのフラットキーは単一の文字列です——"auth.login.title"。ネストされたオブジェクトは、authの中のloginの中のtitleというキーを持つ実際のオブジェクト構造です。ほとんどのライブラリは両方をサポートしていますが、同じファイルで混在させると曖昧さが生じます。
推奨: JSONファイルでは真のネストを使用し、コードでキーにアクセスする場合にのみドット記法を使用してください。同じネストレベル内のキー名にドットを使用しないでください。
{
"auth": {
"login": {
"title": "Sign in"
}
}
}
コードではt('auth.login.title')としてアクセスします——ライブラリがトラバーサルを処理します。
snake_caseとcamelCase
どちらも一般的です。snake_caseはJSONファイルで読みやすいです。単語が視覚的に混在しないためです。camelCaseはJavaScriptの規約に沿っており、FormatJSプロジェクトで一般的です。
{
"submit_button": "Submit",
"error_message": "An error occurred"
}
{
"submitButton": "Submit",
"errorMessage": "An error occurred"
}
どちらか一方を選んでリンターで強制してください。プロジェクト内での不一致は、どちらの選択よりも悪い結果をもたらします。
記述的 vs 簡潔
簡潔なキーは短いですが、すぐにコンテキストを失います:
{
"btn_sub": "Subscribe",
"btn_cncl": "Cancel",
"err_404": "Page not found"
}
記述的なキーは値を読まなくても目的を伝えます:
{
"newsletter_subscribe_button": "Subscribe",
"modal_cancel_button": "Cancel",
"error_page_not_found_title": "Page not found"
}
推奨: UIコンテキスト(コンポーネントタイプまたは場所)とコンテンツタイプ(ボタン、タイトル、説明、プレースホルダー)を含む記述的なキーを使用してください。これにより、コンポーネントコードを読まなくてもキーがどのように使用されているかを理解できます。
階層的なグルーピング
ネストされたJSONを使用する場合、まずUIエリアでグループ化し、次にコンポーネントまたはアクション、次に要素タイプでグループ化します:
{
"checkout": {
"cart": {
"title": "Your cart",
"empty_message": "Your cart is empty",
"item_count_one": "{{count}} item",
"item_count_other": "{{count}} items"
},
"payment": {
"title": "Payment details",
"card_number_label": "Card number",
"expiry_label": "Expiry date",
"submit_button": "Pay now"
}
}
}
JSONでの複数形と変数の処理
ICU MessageFormat(FormatJS、vue-i18nなど)
ICU MessageFormatは、複数形と変数のための最も表現豊かで移植性の高い構文です。FormatJSでネイティブにサポートされており、vue-i18nやその他のライブラリでも設定できます。
{
"file_count": "{count, plural, =0 {No files} one {# file} other {# files}}",
"upload_progress": "Uploading {current} of {total} files",
"last_seen": "Last seen {time, date, relative}",
"user_role": "{role, select, admin {Administrator} editor {Editor} other {Viewer}}"
}
複数形ブランチ内の#記号はフォーマットされたカウントに置き換えられます。selectキーワードは数値ではなく文字列値で分岐します。
i18nextの複数形サフィックス規約
i18nextはキーにサフィックスを付加することで複数形を解決します。英語の場合:
{
"message_count_one": "{{count}} message",
"message_count_other": "{{count}} messages",
"message_count_zero": "No messages"
}
t('message_count', { count: 5 })で呼び出すと、i18nextはmessage_count_otherを選択してcountを補間します。複数形がより多い言語(ロシア語には4つある)では、追加のサフィックスバリアントが定義されます:
{
"file_one": "{{count}} файл",
"file_few": "{{count}} файла",
"file_many": "{{count}} файлов",
"file_other": "{{count}} файлов"
}
CLDRの複数形ルールカテゴリ(zero、one、two、few、many、other)はi18nextが使用するサフィックスにマッピングされます。
変数補間
すべてのライブラリは何らかの形式の変数補間をサポートしています。構文は異なります:
{
"greeting_i18next": "Hello, {{name}}!",
"greeting_icu": "Hello, {name}!",
"greeting_vue": "Hello, {name}!",
"greeting_angular": "Hello, {{ name }}!"
}
i18nextはデフォルトで二重の波括弧を使用します。ICUは単一の波括弧を使用します。Angularの$localizeは、JSON値のランタイム補間構文ではなく、コード内のタグ付きテンプレートリテラルを使用します。
同じプロジェクトで補間構文を混在させないでください。 ライブラリに合った構文を選び、JSONスキーマバリデーションで強制してください。
ツールとバリデーション
翻訳ファイルのためのJSONスキーマ
JSONスキーマ定義を使用すると、CIパイプライン、エディター、pre-commitフックで翻訳ファイルの構造をバリデートできます。フラットなキーと値の翻訳ファイルの最小スキーマは次のとおりです:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": {
"type": "string"
},
"minProperties": 1
}
ネストされたファイルの場合、スキーマは再帰的になります。ほとんどのチームは、構造的バリデーションのためのJSONスキーマと、セマンティックチェックのための専用i18nリンティングツールを組み合わせて使用します。
欠落キーのリンティング
最も一般的な本番i18nバグは、欠落した翻訳キーです——英語には存在するがロケールファイルには存在しないキーです。これにより、フォールバックとしてキー名自体または空の文字列が表示され、UIが壊れます。
i18next-parserはソースコードをスキャンしてt('key')呼び出しを見つけ、翻訳ファイルを生成またはバリデートします:
npx i18next-parser --config i18next-parser.config.js
eslint-plugin-i18n-jsonはJSON翻訳ファイルを直接バリデートします:
npm install --save-dev eslint-plugin-i18n-json
翻訳ファイルバリデーションの基本的なESLint設定:
{
"plugins": ["i18n-json"],
"rules": {
"i18n-json/valid-json": "error",
"i18n-json/sorted-keys": ["warn", { "order": "asc" }],
"i18n-json/identical-keys": [
"error",
{ "filePath": "src/locales/en.json" }
]
}
}
identical-keysルールは、すべてのロケールファイルがリファレンスロケールファイル(通常は英語)と同じキーセットを含んでいるかチェックします。欠落しているキーと存在すべきでない余分なキーの両方にフラグを立てます。
完全性のためのCIチェック
ロケールファイルが不完全な場合にマージをブロックするCIステップにより、翻訳負債の蓄積を防ぎます。シンプルなシェルスクリプトチェック:
#!/usr/bin/env bash
BASE="src/locales/en.json"
LOCALES=("fr" "de" "ja" "es")
for locale in "${LOCALES[@]}"; do
FILE="src/locales/${locale}.json"
BASE_KEYS=$(jq 'keys | length' "$BASE")
LOCALE_KEYS=$(jq 'keys | length' "$FILE")
if [ "$BASE_KEYS" != "$LOCALE_KEYS" ]; then
echo "ERROR: ${FILE} has ${LOCALE_KEYS} keys, expected ${BASE_KEYS}"
exit 1
fi
done
echo "All locale files are complete."
ネストされたファイルにはjqのパストラバーサルが必要です。Better i18nなどのプラットフォームはJSONをネイティブフォーマットとして使用し、CLIを通じて翻訳を同期します。つまり、翻訳がコミットされる前にプラットフォーム側で完全性チェックを強制でき、不完全なファイルのCIレベルのチェックの必要性が減ります。
エディター統合
VS Codeの「i18n Ally」などの拡張機能は、インラインの翻訳プレビュー、欠落キーのハイライト、エディター内での機械翻訳の提案を提供します。これによりバリデーションが前倒しになります——開発者はCIではなくコードを書きながら欠落した翻訳を確認できます。
フォーマット間の移行
JSONからXLIFFへ
XLIFFは専門翻訳会社に翻訳を送る際に必要です。JSONからXLIFF 2.0への変換は、キーと値のペアを<segment>子を持つ<unit>要素にマッピングする作業を含みます。
i18next-convツールは双方向変換を処理します:
npm install -g i18next-conv # JSONからXLIFFへ i18next-conv -l en -s locales/en.json -t locales/en.xliff # XLIFFからJSONへ i18next-conv -l fr -s locales/fr.xliff -t locales/fr.json
FormatJSプロジェクトでは、@formatjs/cliがXLIFFエクスポートを提供します:
npx formatjs compile-folder --ast src/locales/en.json > compiled/en.json
JSONからPOへ
PO(Portable Object)フォーマットはGettextエコシステムで使用され、PoeditやWeblateなどのツールでサポートされています。i18next-convツールはPOもサポートしています:
# JSONからPOへ i18next-conv -l en -s locales/en.json -t locales/en.po # POからJSONへ i18next-conv -l fr -s locales/fr.po -t locales/fr.json
vue-i18nプロジェクトでは、@intlify/vue-i18n-loaderがJSONの代替としてPOファイルを直接サポートしています。これは、VueプロジェクトがGettextを使用するサーバーサイドアプリケーションと翻訳メモリを共有する場合に便利です。
一般的な変換ツール
| ツール | 変換 | 注意 |
|---|---|---|
i18next-conv | JSON ↔ PO、JSON ↔ XLIFF、JSON ↔ CSV | i18nextフォーマットに最も完全 |
@formatjs/cli | FormatJS JSON → XLIFF、抽出されたメッセージ → ロケールファイル | FormatJS/react-intlプロジェクトに必要 |
gettext-converter | PO ↔ JSON(フラット) | シンプル、フレームワークの前提なし |
xliff-simple-merge | XLIFFのマージと分割 | angular/localizeを使用するAngularプロジェクトに便利 |
| オンラインXLIFFエディター | XLIFF ↔ JSON(UIを通じて) | Phrase、Lokalise、Crowdinはすべてインポート/エクスポートを提供 |
フォーマット間で移行する際は、コミットする前に出力をバリデートしてください。自動変換ツールは時々コンテキスト文字列を失ったり、コメントを削除したり、非標準の複数形ルールを持つ言語の複数形を誤って処理したりすることがあります。
FAQ
新しいReactプロジェクトにはどのJSONフォーマットを使うべきか?
react-i18next(最も一般的な選択)を使用している場合、i18next規約に従った名前空間分割のネストされたJSONから始めてください。react-intlまたはFormatJSベースのセットアップを使用している場合は、値にICU MessageFormat構文を持つフラットキーを使用してください。ライブラリの選択がフォーマットを決定します——まずプロジェクト要件に基づいてライブラリを選び、その後ドキュメントの規約に従ってください。
JSONキーは英語文字列自体であるべきか、意味的な識別子であるべきか?
意味的な識別子(auth.login.submit_buttonなど)はほとんどのアプリケーションで好ましいです。英語文字列をキーとして使用すること(Gettextスタイル:t('Submit'))は小規模プロジェクトでは機能しますが、スケール時に問題が生じます。同じ名前空間内で異なるコンテキストを持つ2つの「Submit」ボタンを持つことができず、英語のコピーを変更するとコード内のすべてのキー参照を更新する必要があります。
UIを壊さずにランタイムで欠落した翻訳を処理するには?
すべての主要なi18nライブラリは、キーが欠落している場合に設定されたデフォルトロケールにフォールバックします。フォールバックとして英語ロケールファイルを使用するようにライブラリを設定してください。これにより、フランス語の翻訳が欠落している場合はキー名ではなく英語の文字列が表示されます。開発中に欠落しているキーをログに記録してください(ほとんどのライブラリはmissingKeyHandlerコールバックをサポートしています)。これにより、テスト中に可視化されます。
JSON翻訳ファイルでコメントを使用しても安全か?
標準のJSONはコメントをサポートしていません。一部のツールはJSON5またはJSONC(コメント付きJSON)を拡張としてサポートしていますが、標準のJSONパーサーとの互換性が損なわれます。翻訳者向けのコンテキストで文字列に注釈を付ける必要がある場合は、キーレベルで別のdescriptionフィールドを使用するか(chrome.i18nがそうしているように)、別のメタデータファイルを維持してください。より簡単なアプローチ:インラインコメントなしにコンテキストを伝える記述的なキー名を使用してください。
JSON翻訳ファイルで右から左(RTL)言語を処理するには?
RTL言語(アラビア語、ヘブライ語、ペルシャ語)のJSON構造はLTR言語と同じです——ファイルフォーマットは変わりません。RTLサポートはCSSとレイアウトの問題であり、翻訳ファイルの問題ではありません。i18nライブラリはアクティブなロケールのテキスト方向を検出する方法を提供します(通常はIntl.Locale APIまたはロケールメタデータオブジェクトを通じて)。アプリケーションはそれに応じてドキュメントまたはコンポーネントにdir="rtl"を適用します。RTL固有のCSSはスタイルシートに保持し、翻訳値には含めないでください。
結論
JSON翻訳ファイルは単一の普遍的な標準に従っていません。それを読み込むライブラリの規約に従っています。ライブラリが期待するフォーマットとその設計理由を理解することは、「正しい」アプローチを探すよりも有用です。
実用的なガイダンスはこれです。ReactやVue、またはNode.jsアプリケーションを構築していて、最大のツールと例のエコシステムを求めているなら、i18next規約に従ったネストされたJSONを使用してください。アプリケーションが多くのロケールで複雑な複数形、性の一致、またはリッチなフォーマットを処理する必要があり、式をできるだけ移植性の高いものにしたい場合は、値にICU MessageFormatを使用してください。認知的なオーバーヘッドよりも組織的な構造が重要な小規模プロジェクトや迅速なプロトタイピングにはフラットキーを使用してください。
ファイルの整理、キー命名、CIバリデーションはフォーマットの選択と同様に重要です。一貫した命名と自動的な完全性チェックを持つフラットなファイルは、ツールも強制もない慎重に名前空間化された構造よりも小さなチームによく機能します。
翻訳負債は静かに蓄積されます——ここで欠落したキー、そこで古い文字列——ローカライズされたユーザーが壊れたUI文字列に遭遇するまで続きます。この結果を避けるチームは、翻訳ファイルを他のコードアーティファクトと同様に扱います:プルリクエストでレビューされ、CIでバリデートされ、将来のメンテナーを念頭に置いて整理されます。
参考文献
- i18nextドキュメント:複数形
- i18nextドキュメント:補間
- FormatJS:ICUメッセージ構文
- react-intlドキュメント
- vue-i18nドキュメント
- Chrome拡張機能:chrome.i18n API
- XLIFF 2.0仕様(OASIS)
- Unicode CLDRの複数形ルール
- i18next-conv:変換CLI
- eslint-plugin-i18n-json
- i18n Ally VS Code拡張機能
- ICUメッセージフォーマットリファレンス(Unicode)
最終更新:2026年3月