- 設計
エラーメッセージ設計の書き方 メッセージID体系と例文一覧
エラーメッセージ設計とは、画面に出す文言を「ID・種別・文言・埋め込み項目・表示箇所・対処」の一覧表で管理し、だれが書いても同じ言い回しになるようにルールを決めることです。 文言を画面ごとにばらばらに書くと、同じ入力ミスに対して言い方が3種類ある、といった状態になりやすく、直すときに探す手間も増えます。一覧表とIDがあれば、文言の修正は1か所で済み、問い合わせを受けたときもIDで原因をたどれます。
この記事では、メッセージIDの採番ルール、そのまま表計算ソフトに貼れる一覧表テンプレート、場面別の例文50件、文言ルールの早見表をまとめました。一覧表をPythonで検査して properties と JSON に書き出すコード、Java と JavaScript で {0} を埋めて表示するコードは、実際に動かした結果を載せています。題材は架空の「社内の備品予約システム」です。APIが返すエラー応答の形式ではなく、画面に出す文言とIDの管理に絞っています。
確認環境:Ubuntu 24.04、Python 3.11.15、OpenJDK 21.0.10、Node.js 22.22.2。
エラーメッセージの文言ルール早見表
| 決めること | ルール | 例 |
|---|---|---|
| 文体 | です・ます体にそろえる | {0}を入力してください。 |
| エラー(E)の語尾 | 「〜してください。」で次の行動を示す | {0}は半角数字で入力してください。 |
| 警告(W)の語尾 | 「〜しています。」「〜です。」で状況を伝える | パスワードの有効期限まであと{0}日です。 |
| 確認(Q)の語尾 | 「〜しますか?」「よろしいですか?」で終える | {0}を削除します。…よろしいですか? |
| 完了・案内(I)の語尾 | 「〜しました。」と過去形で言い切る | {0}を登録しました。 |
| 主語 | 項目名を主語にする(「あなたは」「システムは」を主語にしない) | {0}は{1}文字以内で入力してください。 |
| 項目名 | 画面のラベルと同じ言葉を使う | ラベルが「備品名」なら {0} にも「備品名」を渡す |
| プレースホルダ | {0} から順に番号を振り、何が入るかを一覧表に書く |
{0}=項目名; {1}=最大文字数 |
| 原因と対処 | 「何が起きたか。どうすればよいか。」の順で2文以内 | ほかの利用者が{0}を更新しました。最新の内容を読み込んでから、もう一度操作してください。 |
| 入力例 | 形式を求めるときは例を添える | (例:name@example.com) |
| 内部の情報 | テーブル名・例外名・SQLは出さず、問い合わせ番号を出す | 問い合わせ番号{0}を管理者にお伝えください。 |
| 言葉づかい | 利用者を責める言葉(不正・違反・無効)を避け、事実と直し方を書く | 「不正な値です」→「半角数字で入力してください」 |
| 文の長さ | 1文は40字程度まで、全体で2文まで | 長くなるときは対処をヘルプに分ける |
| 句読点 | 文末は「。」、確認は「?」で終える。「!」は使わない | 一覧表の検査で機械的に確かめる |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「エラーメッセージ設計の書き方 メッセージID体系と例文一覧」
メッセージIDの採番ルール
メッセージIDは、種別1文字+機能コード3文字+連番3桁をハイフンでつないだ形にします。IDを見ただけで「どんな性質のメッセージで、どの機能のものか」がわかり、検索もしやすくなります。
E-RSV-001
│ │ └─ 連番(機能コードごとに 001 から)
│ └───── 機能コード(英大文字3文字)
└──────── 種別(E / W / I / Q)
種別の4分類
| 種別 | 意味 | 利用者に求めること | 画面での出し方の例 | ボタン |
|---|---|---|---|---|
| E(Error) | 処理を続けられない | 入力や操作を直す | 項目の下に赤字、または画面上部 | 閉じる |
| W(Warning) | 続けられるが注意が必要 | 内容を確かめる | 画面上部に黄色の帯 | 閉じる |
| I(Information) | 処理の完了・案内 | 特になし | 画面上部に数秒表示 | なし |
| Q(Question) | 実行前の確認 | はい/いいえを選ぶ | 確認ダイアログ | はい・いいえ |
種別をIDの先頭に置くと、画面側はIDの1文字目だけで色・アイコン・ボタンを切り替えられます。
機能コードの決め方
| 機能コード | 対象 | 置くメッセージ |
|---|---|---|
| CMN | 共通 | 必須・桁数・形式など、どの画面でも使う文言 |
| LGN | ログイン | 認証、パスワード、セッション |
| USR | 利用者管理 | 利用者の登録・変更に固有の文言 |
| ITM | 備品管理 | 備品の登録・削除に固有の文言 |
| RSV | 予約 | 予約の重複、状態による制限 |
| FIL | ファイル取込 | アップロード、CSV取込の結果 |
| SYS | システム | 通信、タイムアウト、想定外のエラー |
まず CMN に入れられないかを考え、その機能でしか使わない文言だけを機能別のコードに置きます。 こうすると「{0}を入力してください。」が画面の数だけ増えることを防げます。
採番で決めておくこと
| 項目 | 決め方の例 |
|---|---|
| 連番の桁数 | 3桁(機能コードごとに999件まで)。共通が増えそうなら4桁にする |
| 欠番 | 使わなくなったIDは再利用しない(過去の問い合わせ記録やログと食い違うため) |
| 文言の変更 | 意味が同じ言い回しの修正は同じIDのまま。意味が変わるなら新しいIDを採番する |
| 連番の意味づけ | 連番に意味を持たせない(「100番台は必須」などと決めると、枠が埋まったときに困る) |
| 採番の担当 | 一覧表の最終行に追記する人を1人(または1チーム)に決める |
| 画面への表示 | 文言の末尾にIDを添えるかどうかを決める(例:…入力してください。(E-CMN-001)) |
メッセージ一覧表テンプレート
一覧表は次の7列で作ります。下のコードブロックはタブ区切りなので、コピーして表計算ソフトのセルA1に貼ると、そのまま列に分かれます。
| 列 | 書くこと | 記入例 |
|---|---|---|
| ID | 採番ルールに沿ったID | E-CMN-004 |
| 種別 | E / W / I / Q | E |
| 場面 | 必須・桁数・形式・重複・権限・排他など | 桁数 |
| 文言 | 画面に出す文。可変部分は {0} {1} |
{0}は{1}文字以内で入力してください。 |
| 埋め込み項目 | 番号ごとに何が入るか。無ければ「なし」 | {0}=項目名; {1}=最大文字数 |
| 表示箇所 | 項目の下、画面上部、確認ダイアログなど | 項目の下 |
| 対処 | 利用者が次にすること(問い合わせ対応の手引きにもなる) | 文字数を減らす |
ID 種別 場面 文言 埋め込み項目 表示箇所 対処
E-CMN-001 E 必須 {0}を入力してください。 {0}=項目名 項目の下 未入力の項目を入力する
「埋め込み項目」の列があると、実装する人がどの順番で何を渡すかを迷いません。「対処」の列は、利用者からの問い合わせに答えるときの手引きとしても使えます。
場面別のエラーメッセージ例文50件
備品予約システムを題材にした50件です。内訳はエラー38件、警告3件、確認4件、完了・案内5件です。
| No. | ID | 場面 | 文言 |
|---|---|---|---|
| 1 | E-CMN-001 | 必須 | {0}を入力してください。 |
| 2 | E-CMN-002 | 必須 | {0}を選択してください。 |
| 3 | E-CMN-003 | 必須 | {0}または{1}のどちらかを入力してください。 |
| 4 | E-CMN-004 | 桁数 | {0}は{1}文字以内で入力してください。 |
| 5 | E-CMN-005 | 桁数 | {0}は{1}文字以上で入力してください。 |
| 6 | E-CMN-006 | 桁数 | {0}は{1}桁で入力してください。 |
| 7 | E-CMN-007 | 桁数 | {0}は{1}文字以上{2}文字以内で入力してください。 |
| 8 | E-CMN-008 | 形式 | {0}は半角数字で入力してください。 |
| 9 | E-CMN-009 | 形式 | {0}は半角英数字で入力してください。 |
| 10 | E-CMN-010 | 形式 | {0}の形式を確認してください。(例:name@example.com) |
| 11 | E-CMN-011 | 形式 | {0}は「2026/10/05」の形式で入力してください。 |
| 12 | E-CMN-012 | 形式 | {0}に存在しない日付が入力されています。 |
| 13 | E-CMN-013 | 形式 | {0}に使用できない文字が含まれています:{1} |
| 14 | E-CMN-014 | 範囲 | {0}は{1}から{2}の範囲で入力してください。 |
| 15 | E-CMN-015 | 範囲 | {0}は{1}以降の日付を入力してください。 |
| 16 | E-CMN-016 | 範囲 | {0}は本日以降の日付を入力してください。 |
| 17 | E-CMN-017 | 範囲 | {0}は{1}件まで選択できます。 |
| 18 | E-CMN-018 | 相関 | {0}と{1}が一致していません。もう一度入力してください。 |
| 19 | E-CMN-019 | 重複 | この操作はすでに受け付けています。画面を更新して結果を確認してください。 |
| 20 | E-CMN-020 | 存在 | 指定された{0}は削除されたか、存在しません。一覧から選び直してください。 |
| 21 | E-CMN-021 | 権限 | この操作を行う権限がありません。必要な場合は管理者にお問い合わせください。 |
| 22 | E-CMN-022 | 排他 | ほかの利用者が{0}を更新しました。最新の内容を読み込んでから、もう一度操作してください。 |
| 23 | E-CMN-023 | 排他 | {0}は{1}さんが編集中です。しばらくしてからもう一度お試しください。 |
| 24 | E-USR-001 | 重複 | {0}「{1}」はすでに登録されています。別の{0}を入力してください。 |
| 25 | E-RSV-001 | 重複 | {0}は{1}から{2}の時間帯にすでに予約があります。別の時間帯を選択してください。 |
| 26 | E-RSV-002 | 排他 | この予約は{0}のため、変更できません。 |
| 27 | E-ITM-001 | 存在 | {0}は予約で使用されているため削除できません。先に予約を取り消してください。 |
| 28 | E-FIL-001 | 重複 | {0}行目:{1}がファイル内で重複しています。 |
| 29 | E-FIL-002 | 形式 | ファイルサイズは{0}MB以下にしてください。 |
| 30 | E-FIL-003 | 形式 | アップロードできるファイル形式は{0}です。 |
| 31 | E-LGN-001 | 権限 | ログインIDまたはパスワードが正しくありません。 |
| 32 | E-LGN-002 | 権限 | 入力を{0}回続けて誤ったため、アカウントを一時的にロックしました。{1}分後にもう一度お試しください。 |
| 33 | E-LGN-003 | 権限 | パスワードの有効期限が切れています。新しいパスワードを設定してください。 |
| 34 | E-LGN-004 | タイムアウト | 一定時間操作がなかったため、ログアウトしました。もう一度ログインしてください。 |
| 35 | E-SYS-001 | タイムアウト | 処理に時間がかかっています。しばらくしてからもう一度お試しください。 |
| 36 | E-SYS-002 | タイムアウト | サーバーに接続できませんでした。通信状況を確認して、もう一度お試しください。 |
| 37 | E-SYS-003 | システム | システムエラーが発生しました。解決しない場合は、問い合わせ番号{0}を管理者にお伝えください。 |
| 38 | E-SYS-004 | システム | ただいまメンテナンス中です。{0}ごろに再開する予定です。 |
| 39 | W-RSV-001 | 警告 | {0}の返却予定日を{1}日過ぎています。 |
| 40 | W-LGN-001 | 警告 | パスワードの有効期限まであと{0}日です。 |
| 41 | W-FIL-001 | 警告 | {0}件中{1}件を取り込みました。{2}件はエラーのため取り込んでいません。 |
| 42 | Q-CMN-001 | 確認 | {0}を登録します。よろしいですか? |
| 43 | Q-CMN-002 | 確認 | {0}を削除します。削除すると元に戻せません。よろしいですか? |
| 44 | Q-CMN-003 | 確認 | 入力中の内容は保存されていません。画面を移動しますか? |
| 45 | Q-RSV-001 | 確認 | {0}の予約を取り消します。よろしいですか? |
| 46 | I-CMN-001 | 完了 | {0}を登録しました。 |
| 47 | I-CMN-002 | 完了 | {0}を更新しました。 |
| 48 | I-CMN-003 | 完了 | {0}を削除しました。 |
| 49 | I-CMN-004 | 案内 | 条件に一致する{0}はありません。 |
| 50 | I-FIL-001 | 完了 | {0}件のデータを取り込みました。 |
表計算ソフトに貼れる一覧(タブ区切り・全7列)
上の50件に、埋め込み項目・表示箇所・対処を加えた完全版です。messages.tsv という名前で保存すると、後半のPythonコードでそのまま検査できます。
ID 種別 場面 文言 埋め込み項目 表示箇所 対処
E-CMN-001 E 必須 {0}を入力してください。 {0}=項目名 項目の下 未入力の項目を入力する
E-CMN-002 E 必須 {0}を選択してください。 {0}=項目名 項目の下 選択肢から1つ選ぶ
E-CMN-003 E 必須 {0}または{1}のどちらかを入力してください。 {0}=項目名; {1}=項目名 項目の下 どちらか一方を入力する
E-CMN-004 E 桁数 {0}は{1}文字以内で入力してください。 {0}=項目名; {1}=最大文字数 項目の下 文字数を減らす
E-CMN-005 E 桁数 {0}は{1}文字以上で入力してください。 {0}=項目名; {1}=最小文字数 項目の下 文字数を増やす
E-CMN-006 E 桁数 {0}は{1}桁で入力してください。 {0}=項目名; {1}=桁数 項目の下 桁数を合わせる
E-CMN-007 E 桁数 {0}は{1}文字以上{2}文字以内で入力してください。 {0}=項目名; {1}=最小文字数; {2}=最大文字数 項目の下 文字数を範囲内にする
E-CMN-008 E 形式 {0}は半角数字で入力してください。 {0}=項目名 項目の下 半角数字に直す
E-CMN-009 E 形式 {0}は半角英数字で入力してください。 {0}=項目名 項目の下 半角英数字に直す
E-CMN-010 E 形式 {0}の形式を確認してください。(例:name@example.com) {0}=項目名 項目の下 例の形式に合わせる
E-CMN-011 E 形式 {0}は「2026/10/05」の形式で入力してください。 {0}=項目名 項目の下 年/月/日の形式に直す
E-CMN-012 E 形式 {0}に存在しない日付が入力されています。 {0}=項目名 項目の下 カレンダーにある日付に直す
E-CMN-013 E 形式 {0}に使用できない文字が含まれています:{1} {0}=項目名; {1}=該当文字 項目の下 表示された文字を取り除く
E-CMN-014 E 範囲 {0}は{1}から{2}の範囲で入力してください。 {0}=項目名; {1}=最小値; {2}=最大値 項目の下 範囲内の値に直す
E-CMN-015 E 範囲 {0}は{1}以降の日付を入力してください。 {0}=項目名; {1}=比較先の項目名 項目の下 日付の前後を直す
E-CMN-016 E 範囲 {0}は本日以降の日付を入力してください。 {0}=項目名 項目の下 本日以降の日付にする
E-CMN-017 E 範囲 {0}は{1}件まで選択できます。 {0}=項目名; {1}=最大件数 項目の下 選択を減らす
E-CMN-018 E 相関 {0}と{1}が一致していません。もう一度入力してください。 {0}=項目名; {1}=項目名 項目の下 同じ値を入力する
E-CMN-019 E 重複 この操作はすでに受け付けています。画面を更新して結果を確認してください。 なし 画面上部 画面を更新して確認する
E-CMN-020 E 存在 指定された{0}は削除されたか、存在しません。一覧から選び直してください。 {0}=対象名 画面上部 一覧に戻って選び直す
E-CMN-021 E 権限 この操作を行う権限がありません。必要な場合は管理者にお問い合わせください。 なし 画面上部 管理者に権限を依頼する
E-CMN-022 E 排他 ほかの利用者が{0}を更新しました。最新の内容を読み込んでから、もう一度操作してください。 {0}=対象名 画面上部 再読み込みして入力し直す
E-CMN-023 E 排他 {0}は{1}さんが編集中です。しばらくしてからもう一度お試しください。 {0}=対象名; {1}=利用者名 画面上部 時間をおいて操作する
E-USR-001 E 重複 {0}「{1}」はすでに登録されています。別の{0}を入力してください。 {0}=項目名; {1}=入力値 項目の下 別の値を入力する
E-RSV-001 E 重複 {0}は{1}から{2}の時間帯にすでに予約があります。別の時間帯を選択してください。 {0}=備品名; {1}=開始時刻; {2}=終了時刻 項目の下 空いている時間帯を選ぶ
E-RSV-002 E 排他 この予約は{0}のため、変更できません。 {0}=状態名 画面上部 状態を確認する
E-ITM-001 E 存在 {0}は予約で使用されているため削除できません。先に予約を取り消してください。 {0}=備品名 画面上部 関連する予約を取り消す
E-FIL-001 E 重複 {0}行目:{1}がファイル内で重複しています。 {0}=行番号; {1}=項目名 取込結果の一覧 該当行を直して再取込する
E-FIL-002 E 形式 ファイルサイズは{0}MB以下にしてください。 {0}=上限サイズ 項目の下 ファイルを小さくする
E-FIL-003 E 形式 アップロードできるファイル形式は{0}です。 {0}=拡張子の一覧 項目の下 対応する形式で保存し直す
E-LGN-001 E 権限 ログインIDまたはパスワードが正しくありません。 なし 画面上部 入力し直す
E-LGN-002 E 権限 入力を{0}回続けて誤ったため、アカウントを一時的にロックしました。{1}分後にもう一度お試しください。 {0}=回数; {1}=待ち時間(分) 画面上部 時間をおいてログインする
E-LGN-003 E 権限 パスワードの有効期限が切れています。新しいパスワードを設定してください。 なし 画面上部 パスワードを変更する
E-LGN-004 E タイムアウト 一定時間操作がなかったため、ログアウトしました。もう一度ログインしてください。 なし ログイン画面の上部 ログインし直す
E-SYS-001 E タイムアウト 処理に時間がかかっています。しばらくしてからもう一度お試しください。 なし 画面上部 時間をおいて操作する
E-SYS-002 E タイムアウト サーバーに接続できませんでした。通信状況を確認して、もう一度お試しください。 なし 画面上部 通信状況を確認する
E-SYS-003 E システム システムエラーが発生しました。解決しない場合は、問い合わせ番号{0}を管理者にお伝えください。 {0}=問い合わせ番号 エラー画面 番号を控えて管理者に連絡する
E-SYS-004 E システム ただいまメンテナンス中です。{0}ごろに再開する予定です。 {0}=再開予定時刻 メンテナンス画面 再開時刻まで待つ
W-RSV-001 W 警告 {0}の返却予定日を{1}日過ぎています。 {0}=備品名; {1}=超過日数 一覧の行 返却するか期間を延長する
W-LGN-001 W 警告 パスワードの有効期限まであと{0}日です。 {0}=残り日数 画面上部 期限までに変更する
W-FIL-001 W 警告 {0}件中{1}件を取り込みました。{2}件はエラーのため取り込んでいません。 {0}=全件数; {1}=取込件数; {2}=エラー件数 取込結果の上部 エラー行を直して再取込する
Q-CMN-001 Q 確認 {0}を登録します。よろしいですか? {0}=対象名 確認ダイアログ はい/いいえを選ぶ
Q-CMN-002 Q 確認 {0}を削除します。削除すると元に戻せません。よろしいですか? {0}=対象名 確認ダイアログ はい/いいえを選ぶ
Q-CMN-003 Q 確認 入力中の内容は保存されていません。画面を移動しますか? なし 確認ダイアログ 移動する/とどまるを選ぶ
Q-RSV-001 Q 確認 {0}の予約を取り消します。よろしいですか? {0}=備品名 確認ダイアログ はい/いいえを選ぶ
I-CMN-001 I 完了 {0}を登録しました。 {0}=対象名 画面上部 なし
I-CMN-002 I 完了 {0}を更新しました。 {0}=対象名 画面上部 なし
I-CMN-003 I 完了 {0}を削除しました。 {0}=対象名 画面上部 なし
I-CMN-004 I 案内 条件に一致する{0}はありません。 {0}=対象名 一覧の位置 条件を変えて検索する
I-FIL-001 I 完了 {0}件のデータを取り込みました。 {0}=取込件数 画面上部 なし
原因と対処の書き分け:直す前と後
文言は「何が起きたか(原因)」と「どうすればよいか(対処)」のどちらを書くかを場面ごとに決めます。
| 場面 | 書くもの | 理由 |
|---|---|---|
| 入力チェック(必須・桁数・形式) | 対処だけ | 原因は項目を見ればわかるため、直し方を1文で伝える |
| 重複・存在・排他 | 原因+対処 | 利用者の操作だけでは原因が見えないため |
| 権限 | 原因+相談先 | 利用者自身では解決できないため、頼る先を示す |
| ログイン | 原因をぼかす | IDとパスワードのどちらが違うかを示さない |
| タイムアウト・通信 | 状況+対処 | 利用者に落ち度がないことが伝わる言い方にする |
| 想定外のエラー | 状況+問い合わせ番号 | 内部の詳細はログに残し、画面には番号だけを出す |
同じ場面を、直す前と後で比べます。
| 場面 | 直す前 | 直した後 | 直した点 |
|---|---|---|---|
| 必須 | 入力エラーです。 | 備品名を入力してください。 | どの項目を、どうするかを書く |
| 形式 | 不正な値が入力されました。 | 数量は半角数字で入力してください。 | 責める言葉をやめ、直し方を書く |
| 桁数 | 文字数オーバー | 備考は200文字以内で入力してください。 | 上限の数字を出し、文として完結させる |
| 重複 | 一意制約違反が発生しました。 | ログインID「sato」はすでに登録されています。別のログインIDを入力してください。 | 内部の用語を画面の言葉に置き換える |
| 排他 | 更新できませんでした。 | ほかの利用者が予約を更新しました。最新の内容を読み込んでから、もう一度操作してください。 | 原因と次の操作を書く |
| 権限 | 403 Forbidden | この操作を行う権限がありません。必要な場合は管理者にお問い合わせください。 | 番号ではなく言葉で伝え、相談先を示す |
| 想定外 | NullPointerException | システムエラーが発生しました。解決しない場合は、問い合わせ番号20261005-0042を管理者にお伝えください。 | 例外名を出さず、番号でログと結びつける |
一覧表をPythonで検査して properties と JSON に書き出す
一覧表は人が編集するので、IDの重複や {0} の数え間違いが入りやすいところです。書き出しの前に機械で確かめると、画面で気づく前に直せます。次のコードは標準ライブラリだけで動きます。
"""メッセージ一覧(TSV)を検査し、properties と JSON に書き出す。
使い方: python3 check_messages.py messages.tsv
"""
import csv
import json
import re
import sys
from collections import Counter
ID_RULE = re.compile(r"^([EWIQ])-([A-Z]{3})-(\d{3})$")
PLACEHOLDER = re.compile(r"\{(\d+)\}")
ENDINGS = {"E": ("。", ")"), "W": ("。",), "I": ("。",), "Q": ("?",)}
def check(rows):
errors = []
ids = Counter(r["ID"] for r in rows)
for no, r in enumerate(rows, start=2): # 1行目は見出し
mid, text = r["ID"], r["文言"]
m = ID_RULE.match(mid)
if not m:
errors.append(f"{no}行目 {mid}: IDの形式が規則と違います")
continue
if m.group(1) != r["種別"]:
errors.append(f"{no}行目 {mid}: IDの先頭と種別 {r['種別']} が合いません")
if ids[mid] > 1:
errors.append(f"{no}行目 {mid}: IDが重複しています")
used = sorted({int(n) for n in PLACEHOLDER.findall(text)})
if used != list(range(len(used))):
errors.append(f"{no}行目 {mid}: 番号が0からの連番ではありません {used}")
declared = [] if r["埋め込み項目"] == "なし" else r["埋め込み項目"].split(";")
if len(declared) != len(used):
errors.append(
f"{no}行目 {mid}: 文言の埋め込み {len(used)} 個と、"
f"埋め込み項目欄 {len(declared)} 個が合いません"
)
if not text.endswith(ENDINGS[m.group(1)]) and ":{" not in text:
errors.append(f"{no}行目 {mid}: 語尾が種別 {m.group(1)} の規則と違います")
return errors
def main(path):
with open(path, encoding="utf-8", newline="") as f:
rows = list(csv.DictReader(f, delimiter="\t"))
errors = check(rows)
for e in errors:
print(e)
if errors:
print(f"検査結果: {len(errors)} 件の指摘があります(書き出しは行いません)")
return 1
with open("messages.properties", "w", encoding="utf-8") as f:
for r in rows:
f.write(f"{r['ID']}={r['文言']}\n")
with open("messages.json", "w", encoding="utf-8") as f:
json.dump({r["ID"]: r["文言"] for r in rows}, f, ensure_ascii=False, indent=2)
f.write("\n")
kinds = Counter(r["種別"] for r in rows)
print(f"検査結果: 指摘なし({len(rows)} 件)")
print("種別ごとの件数:", ", ".join(f"{k}={kinds[k]}" for k in "EWQI"))
print("書き出し: messages.properties, messages.json")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1]))
検査している内容は6つです。IDの形式、IDの先頭と種別の一致、IDの重複、{番号} が0からの連番になっているか、文言の {番号} の個数と「埋め込み項目」欄の個数が合うか、そして種別ごとの語尾です(値をそのまま末尾に出す :{1} の形は語尾の検査から外しています)。
50件の一覧に対して実行した結果です。
$ python3 check_messages.py messages.tsv
検査結果: 指摘なし(50 件)
種別ごとの件数: E=38, W=3, Q=4, I=5
書き出し: messages.properties, messages.json
書き出された2つのファイルの先頭です。
E-CMN-001={0}を入力してください。
E-CMN-002={0}を選択してください。
E-CMN-003={0}または{1}のどちらかを入力してください。
{
"E-CMN-001": "{0}を入力してください。",
"E-CMN-002": "{0}を選択してください。",
"E-CMN-003": "{0}または{1}のどちらかを入力してください。",
指摘が出る例
わざと規則に合わない行を混ぜた bad.tsv を用意して、検査が働くことを確かめます。
ID 種別 場面 文言 埋め込み項目 表示箇所 対処
E-CMN-001 E 必須 {0}を入力してください。 {0}=項目名 項目の下 未入力の項目を入力する
E-CMN-001 E 桁数 {0}は{2}文字以内で入力してください {0}=項目名 項目の下 文字数を減らす
ERR-12 E 形式 形式が違います。 なし 項目の下 直す
W-CMN-001 E 確認 {0}を登録します。よろしいですか? {0}=対象名 確認ダイアログ 選ぶ
$ python3 check_messages.py bad.tsv
2行目 E-CMN-001: IDが重複しています
3行目 E-CMN-001: IDが重複しています
3行目 E-CMN-001: 番号が0からの連番ではありません [0, 2]
3行目 E-CMN-001: 文言の埋め込み 2 個と、埋め込み項目欄 1 個が合いません
3行目 E-CMN-001: 語尾が種別 E の規則と違います
4行目 ERR-12: IDの形式が規則と違います
5行目 W-CMN-001: IDの先頭と種別 E が合いません
5行目 W-CMN-001: 語尾が種別 W の規則と違います
検査結果: 8 件の指摘があります(書き出しは行いません)
指摘が1件でもあると書き出しを止め、終了コード1を返します。この検査をビルドやテストの手順に入れておくと、一覧表の更新と同時に確かめられます。
書き出したファイルから文言を表示する
Java:ResourceBundle と MessageFormat
messages.properties をクラスパスに置き、ResourceBundle で読み込んで MessageFormat で {0} を埋めます。
import java.text.MessageFormat;
import java.util.ResourceBundle;
public class ShowMessage {
public static void main(String[] args) {
// クラスパス上の messages.properties(UTF-8)を読み込む
ResourceBundle bundle = ResourceBundle.getBundle("messages");
String p1 = bundle.getString("E-CMN-001");
System.out.println(MessageFormat.format(p1, "備品名"));
String p4 = bundle.getString("E-CMN-004");
System.out.println(MessageFormat.format(p4, "備考", 200));
// 数値をそのまま渡すと桁区切りが付く
System.out.println(MessageFormat.format(p4, "備考", 2000));
// 文字列にして渡すと、そのまま表示される
System.out.println(MessageFormat.format(p4, "備考", String.valueOf(2000)));
// アポストロフィ1個は引用の記号として扱われ、{0} が置き換わらない
System.out.println(MessageFormat.format("Can't open {0}.", "list.csv"));
// 2個重ねると、1個のアポストロフィとして表示される
System.out.println(MessageFormat.format("Can''t open {0}.", "list.csv"));
}
}
$ javac -encoding UTF-8 ShowMessage.java
$ java -Dstdout.encoding=UTF-8 ShowMessage
備品名を入力してください。
備考は200文字以内で入力してください。
備考は2,000文字以内で入力してください。
備考は2000文字以内で入力してください。
Cant open {0}.
Can't open list.csv.
UTF-8で保存した properties を、Java 21 の ResourceBundle でそのまま読めました。結果から、MessageFormat を使うときの注意点が2つわかります。
| 起きること | 例 | 対応 |
|---|---|---|
| 数値を渡すと桁区切りが付く | 2000 → 2,000 |
桁区切りが不要な値(ID、年、桁数)は文字列にして渡す |
| アポストロフィ1個は引用の記号になる | Can't open {0}. → Cant open {0}. |
文言の中では2個重ねて '' と書く |
日本語の文言ではアポストロフィはほとんど使いませんが、英語版のメッセージを同じ仕組みで管理するときにつまずきやすい点です。一覧表の検査に「アポストロフィが1個だけの文言を指摘する」ルールを足しておくと安心です。
JavaScript:JSON を読み込んで置き換える
画面側では messages.json を読み込み、{番号} を引数で置き換える小さな関数を用意します。
import { readFileSync } from "node:fs";
const messages = JSON.parse(readFileSync("messages.json", "utf8"));
// {0} {1} … を引数で置き換える。IDが無いときはIDをそのまま返して気づけるようにする
function msg(id, ...args) {
const template = messages[id];
if (template === undefined) return `[${id}]`;
return template.replace(/\{(\d+)\}/g, (whole, n) =>
n < args.length ? String(args[n]) : whole
);
}
console.log(msg("E-CMN-004", "備考", 2000));
console.log(msg("E-RSV-001", "プロジェクター", "13:00", "15:00"));
console.log(msg("Q-CMN-002", "備品「プロジェクター」"));
console.log(msg("E-CMN-999", "備考"));
$ node show_message.mjs
備考は2000文字以内で入力してください。
プロジェクターは13:00から15:00の時間帯にすでに予約があります。別の時間帯を選択してください。
備品「プロジェクター」を削除します。削除すると元に戻せません。よろしいですか?
[E-CMN-999]
一覧に無いIDを渡したときは、空文字ではなく [E-CMN-999] のようにIDを返しています。画面にIDがそのまま出るので、文言の登録もれにすぐ気づけます。
メッセージ一覧を保ち続けるためのチェックリスト
| 確認すること | 見るところ |
|---|---|
| 同じ意味の文言が2つ無いか | 文言の列を並べ替えて、似た文を見比べる |
画面のラベルと {0} に渡す項目名が同じか |
画面設計書の項目名と照らし合わせる |
| 文言を変えたときにIDを変える必要があるか | 意味が変わったら新しいIDにする |
| 使わなくなったIDを消していないか | 行は残して「廃止」と書き、番号は再利用しない |
| E のメッセージに次の行動が書かれているか | 語尾の検査で確かめる |
| 想定外のエラーに内部の情報が出ていないか | SYS のメッセージを読み直す |
| 一覧表と properties/JSON が一致しているか | 手で直さず、毎回スクリプトで書き出す |
よくある質問
Q. メッセージIDは画面に表示したほうがよいですか? A. 問い合わせを受ける運用があるなら、文言の末尾に小さく添えると原因をたどりやすくなります。利用者が一般の方で、番号が不安を与えそうな場合は、想定外のエラー(SYS)だけ問い合わせ番号を出し、ほかはログにIDを残す形でも十分です。
Q. 「{0}を入力してください。」のような共通文言と、項目ごとの専用文言はどちらがよいですか? A. まず共通文言で始めるのがおすすめです。件数が少なく保て、言い回しがそろいます。「てにをは」が不自然になる項目や、入力例を添えたい項目だけ専用の文言を足します。
Q. プレースホルダは {0} と {name} のどちらがよいですか?
A. Java の MessageFormat のように番号で埋める仕組みを使うなら {0}、画面側のライブラリが名前つきに対応しているなら {name} が読みやすくなります。どちらの場合も、一覧表の「埋め込み項目」列に何が入るかを書いておくことが大切です。
Q. 警告(W)と確認(Q)はどう使い分けますか? A. 利用者の選択を待つなら Q、知らせるだけで処理が進むなら W です。「保存されていません。画面を移動しますか?」のように答えを求める文は Q に分類します。
まとめ
- メッセージIDは「種別(E/W/I/Q)+機能コード3文字+連番3桁」にすると、IDだけで性質と機能がわかる
- 一覧表は「ID・種別・場面・文言・埋め込み項目・表示箇所・対処」の7列で作り、タブ区切りで表計算ソフトと行き来させる
- どの画面でも使う文言は共通(CMN)にまとめ、機能に固有の文言だけを機能別のコードに置く
- エラーは「〜してください。」、確認は「〜ますか?」、完了は「〜しました。」と、種別ごとに語尾をそろえる
- 入力チェックは対処だけ、重複・排他・権限は原因と対処を2文以内で書く
- 一覧表は書き出しの前にスクリプトで検査し、IDの重複と
{0}の数え間違いを見つける MessageFormatは数値に桁区切りを付け、アポストロフィ1個を引用の記号として扱うので、渡し方と書き方を決めておく
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「エラーメッセージ設計の書き方 メッセージID体系と例文一覧」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。