エラーメッセージ設計とは、画面に出す文言を「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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?