メッセージ定義書とは
メッセージ定義書は、システムが出力するすべてのメッセージをコード・テキスト・表示条件・重要度の組み合わせで一覧管理する設計書です。バリデーションエラー・システムエラー・確認ダイアログ・メール通知テンプレートなど、ユーザーが目にするすべての文言を事前に定義・管理します。
💡 メッセージを設計書で管理する理由
メッセージ文言をソースコードに直接埋め込むと、メッセージの変更のたびにコードを修正してデプロイが必要になります。また、複数の開発者が実装すると「必須項目を入力してください」「{項目名}は必須です」「この項目を入力してください」のように表現がバラバラになります。メッセージ定義書でコード化・一元管理することで、変更の柔軟性とユーザー体験の一貫性を確保します。
① メッセージコード体系
メッセージコードは「メッセージ種別 + 連番」で体系化します。
| プレフィックス | 種別 | 用途 | 例 |
|---|---|---|---|
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-002 | DB接続エラー | 一時的にサービスが利用できません。管理者にお問い合わせください。(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) | リソース種別・ID | 404 |
④ 通知・確認メッセージ定義
| コード | 表示場面 | メッセージ | ボタン |
|---|---|---|---|
| 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メッセージファイルを自動生成
"""
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")