メッセージ定義書とは

メッセージ定義書は、システムが出力するすべてのメッセージをコード・テキスト・表示条件・重要度の組み合わせで一覧管理する設計書です。バリデーションエラー・システムエラー・確認ダイアログ・メール通知テンプレートなど、ユーザーが目にするすべての文言を事前に定義・管理します。

💡 メッセージを設計書で管理する理由

メッセージ文言をソースコードに直接埋め込むと、メッセージの変更のたびにコードを修正してデプロイが必要になります。また、複数の開発者が実装すると「必須項目を入力してください」「{項目名}は必須です」「この項目を入力してください」のように表現がバラバラになります。メッセージ定義書でコード化・一元管理することで、変更の柔軟性とユーザー体験の一貫性を確保します。

① メッセージコード体系

メッセージコードは「メッセージ種別 + 連番」で体系化します。

プレフィックス種別用途
VAL-バリデーション入力値の検証エラーVAL-001〜VAL-099
ERR-システムエラーサーバーエラー・DB障害・外部API障害ERR-001〜ERR-099
WARN-警告推奨しない操作の警告WARN-001〜WARN-049
INFO-情報(確認)確認ダイアログ・完了通知INFO-001〜INFO-099
MAIL-メール通知メール件名・本文テンプレートMAIL-001〜MAIL-049

② バリデーションエラーメッセージ定義

入力検証エラーは「項目名」「ルール」「メッセージ」の3つを紐付けて定義します。

コードトリガー条件表示メッセージ(日本語)パラメータ
VAL-001必須項目が未入力{field}は必須項目です{field}: 項目名
VAL-002最大文字数超過{field}は{max}文字以内で入力してください{field}/{max}
VAL-003最小文字数未満{field}は{min}文字以上で入力してください{field}/{min}
VAL-004形式不正(メールアドレス)メールアドレスの形式が正しくありませんなし
VAL-005形式不正(電話番号)電話番号はハイフンなしの10〜11桁の数字で入力してくださいなし
VAL-006形式不正(郵便番号)郵便番号は「000-0000」の形式で入力してくださいなし
VAL-007数値範囲外{field}は{min}以上{max}以下の値を入力してください{field}/{min}/{max}
VAL-008日付形式不正日付は「YYYY/MM/DD」の形式で入力してくださいなし
VAL-009日付順序不正(開始>終了)終了日は開始日以降の日付を入力してくださいなし
VAL-010重複チェックエラー{field}「{value}」はすでに登録されています{field}/{value}
VAL-011ファイルサイズ超過ファイルサイズは{max_mb}MB以内にしてください(現在: {actual_mb}MB){max_mb}/{actual_mb}
VAL-012ファイル形式不正アップロード可能なファイル形式は{formats}です{formats}: "PDF, Excel"等

③ システムエラーメッセージ定義

システムエラーはユーザー向け表示メッセージと開発者向けログメッセージを分けて定義します。

コード発生条件ユーザー表示メッセージログ出力内容HTTP
ERR-001予期しないサーバーエラーシステムエラーが発生しました。しばらく経ってから再度お試しください。(ERR-001)スタックトレース全文500
ERR-002DB接続エラー一時的にサービスが利用できません。管理者にお問い合わせください。(ERR-002)DB接続エラー詳細503
ERR-003外部API呼び出し失敗外部サービスとの通信に失敗しました。時間をおいて再度お試しください。(ERR-003)API名・URL・ステータスコード502
ERR-004認証トークン期限切れセッションが期限切れになりました。再度ログインしてください。ユーザーID・トークン失効時刻401
ERR-005権限なしこの操作を行う権限がありません。管理者にお問い合わせください。(ERR-005)ユーザーID・アクセスURL・ロール403
ERR-006リソースが存在しない指定されたデータが見つかりません。(ERR-006)リソース種別・ID404

④ 通知・確認メッセージ定義

コード表示場面メッセージボタン
INFO-001登録完了{name}を登録しました。閉じる
INFO-002更新完了{name}の情報を更新しました。閉じる
INFO-003削除確認{name}を削除します。この操作は取り消せません。よろしいですか?削除する / キャンセル
INFO-004削除完了{name}を削除しました。閉じる
WARN-001未保存で離脱しようとした入力内容が保存されていません。ページを離れてもよろしいですか?離れる / 戻る
WARN-002大量データの操作確認{count}件のデータに変更を適用します。よろしいですか?実行する / キャンセル

⑤ メール通知テンプレート定義

コード送信トリガー件名主な本文変数
MAIL-001ユーザー登録完了【DevSystem】アカウント登録のご確認{user_name}, {verify_url}
MAIL-002パスワードリセット申請【DevSystem】パスワード再設定のご案内{user_name}, {reset_url}, {expire_time}
MAIL-003注文受付【DevSystem】ご注文を受け付けました(注文番号: {order_no}){user_name}, {order_no}, {order_date}, {items}, {total}
MAIL-004出荷通知【DevSystem】ご注文の商品を発送しました(注文番号: {order_no}){user_name}, {order_no}, {tracking_no}
MAIL-005バッチエラー通知(管理者)【ALERT】バッチ処理エラー: {batch_id} / {batch_name}{batch_id}, {error_time}, {error_message}, {log_url}

⑥ 多言語対応方針

将来的な多言語対応を視野に入れた設計方針を定義します。

  • 言語ファイル形式: Django のlocale/ja/LC_MESSAGES/django.po または i18n-ally 対応の JSON形式で管理
  • メッセージコードを翻訳キーとして使用: フロントエンド・バックエンドともにメッセージコードを翻訳キーとして扱い、言語ファイルから実際の文字列を取得する構成
  • Phase 1(本プロジェクト): 日本語のみ対応。ただし将来の多言語対応を容易にするため、文字列のハードコードは避け言語ファイルに集約

Python Tips — YAMLメッセージ定義からDjangoメッセージファイルを自動生成

Python — メッセージ定義YAML → Django .poファイル生成
"""
YAMLで管理したメッセージ定義をDjangoの.poファイルとJSONに変換するスクリプト。
pip install pyyaml

messages.yaml の例:
  VAL-001:
    ja: "{field}は必須項目です"
    context: "バリデーションエラー: 必須チェック"
  ERR-001:
    ja: "システムエラーが発生しました。しばらく経ってから再度お試しください。(ERR-001)"
    context: "サーバー内部エラー"
"""
import yaml
import json
from pathlib import Path
from datetime import datetime


def load_messages(yaml_path: str) -> dict:
    """YAMLメッセージ定義を読み込む"""
    with open(yaml_path, encoding="utf-8") as f:
        return yaml.safe_load(f)


def generate_django_po(messages: dict, lang: str, output_path: str) -> None:
    """Django .poファイルを生成する"""
    now = datetime.now().strftime("%Y-%m-%d %H:%M+0900")
    lines = [
        '# Django messages file',
        f'# Generated: {now}',
        '# charset=UTF-8',
        'msgid ""',
        'msgstr ""',
        '"Content-Type: text/plain; charset=UTF-8\\n"',
        '"Content-Transfer-Encoding: 8bit\\n"',
        '',
    ]
    for code, data in messages.items():
        text = data.get(lang, "")
        context = data.get("context", "")
        if context:
            lines.append(f'# {context}')
        lines.append(f'#: message_code:{code}')
        lines.append(f'msgid "{code}"')
        # テキスト内のダブルクォートをエスケープ
        escaped = text.replace('\\', '\\\\').replace('"', '\\"')
        lines.append(f'msgstr "{escaped}"')
        lines.append('')

    Path(output_path).parent.mkdir(parents=True, exist_ok=True)
    Path(output_path).write_text('\n'.join(lines), encoding='utf-8')
    print(f"✅ .poファイル生成: {output_path} ({len(messages)}件)")


def generate_json(messages: dict, lang: str, output_path: str) -> None:
    """フロントエンド用JSONメッセージファイルを生成する"""
    result = {code: data.get(lang, "") for code, data in messages.items()}
    Path(output_path).parent.mkdir(parents=True, exist_ok=True)
    with open(output_path, 'w', encoding='utf-8') as f:
        json.dump(result, f, ensure_ascii=False, indent=2)
    print(f"✅ JSONファイル生成: {output_path} ({len(result)}件)")


def validate_placeholders(messages: dict) -> list[str]:
    """プレースホルダーの書式検証({field}形式の抜け漏れを確認)"""
    import re
    errors = []
    for code, data in messages.items():
        for lang, text in data.items():
            if lang in ("context",):
                continue
            # {xxx}形式のプレースホルダーを検出
            placeholders = re.findall(r'\{([^}]+)\}', text)
            if placeholders:
                # Pythonのstr.formatが成功するか検証
                try:
                    dummy = {p: f"[{p}]" for p in placeholders}
                    text.format(**dummy)
                except (KeyError, ValueError) as e:
                    errors.append(f"{code}[{lang}]: プレースホルダーエラー: {e}")
    return errors


if __name__ == "__main__":
    YAML_PATH = "messages.yaml"
    messages = load_messages(YAML_PATH)

    # バリデーション
    errors = validate_placeholders(messages)
    if errors:
        print("❌ プレースホルダーエラーが見つかりました:")
        for e in errors:
            print(f"  - {e}")
    else:
        print("✅ プレースホルダーバリデーション: OK")

    # Django .po ファイル生成
    generate_django_po(messages, lang="ja", output_path="locale/ja/LC_MESSAGES/messages.po")
    # フロントエンド用 JSON 生成
    generate_json(messages, lang="ja", output_path="frontend/src/locales/ja/messages.json")