WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

MVC5 でエラーメッセージが英語

by WebSurfer 2019年2月6日 12:48

Visual Studio Community 2015 のテンプレートで [MVC] を選択して生成した ASP.NET MVC5 アプリケーションでは、そのままではデフォルトで表示されるエラーメッセージが英語になってしまいます。その理由と日本語にする方法を書きます。

エラーメッセージ

上の画像を表示したコードは先の記事「日付と通貨の書式設定」と同じものです(ただし、View から DisplayFor は削除しています)。

一番上のテキストボックスに "1c" という数字としては無効な文字列を入力するとクライアント側の検証で NG となり、"The field ID must be a number." という英語のエラーメッセージが表示されます。(IE11 の場合です。動作はブラウザによって異なります)

Model のプロパティに付与したデータアノテーション属性には、先の記事に書いコードの通り、そのようなエラーメッセージは設定していませんが、html ソースには以下のように data-val-number="The field ID must be a number." という属性が設定されます。上の画像ではその設定値が表示されています。

<input class="form-control text-box single-line" 
    data-val="true" 
    data-val-number="The field ID must be a number." 
    data-val-regex="数字 1 ~ 5 文字" 
    data-val-regex-pattern="^\d{1,5}$" 
    data-val-required="ID は必須" 
    id="ID" name="ID" 
    type="number" 
    value="1" />

なぜ "The field ID must be a number." というような英語になってしまうかと言うと、日本語のサテライトアセンブリ System.Web.Mvc.resources.dll がインストールされてないからです。

(ちなみに、Visual Studio 2010 のテンプレートで作る MVC4 は自動的に日本語のサテライトアセンブリ System.Web.Mvc.resources.dll が bin/ja フォルダに配置されます。MVC3 も、仕組みは不明ですが、エラーメッセージは日本語になります)

解決するには、NuGet パッケージ管理で Microsoft.AspNer.Mvc.ja をインストールしてやるのが簡単かつ確実だと思います。以下の画像を見てください。

NuGet パッケージ追加

それにより、プロジェクトの bin フォルダに ja フォルダが追加され、その中に日本語のサテライトアセンブリが追加されます。

日本語のサテライトアセンブリ

上の画像の ja フォルダの中の EntityFramework 関係のアセンブリは NuGet パッケージ Microsoft.AspNer.Mvc.ja をインストールする際に System.Web.Mvc.resources.dll と同時にインストールされたものです。(依存関係は不明)

インストール済みの他のパッケージが Microsoft.AspNet.Mvc.ja に依存し、更新が必要な場合は同時に更新されます。ちなみに、自分の環境で v5.2.7 をインストールしたら、Microsoft.AspNet.Mvc.5.2.3 ⇒ 5.2.7、Microsoft.AspNet.Razor.3.2.3 ⇒ 3.2.7、Microsoft.AspNet.WebPages.3.2.3 ⇒ 3.2.7 というように更新されました。

【注意】

日本語のサテライトアセンブリをインストールしたら、web.config の system.web 要素内に globalization 要素を追加し、その Culture, UICulture 属性を "auto" に設定するのを忘れないようにしてください。

Culture, UICulture を "auto" に設定すると、ASP.NET は、ブラウザから送信されてくる要求ヘッダに含まれる Accept-Language の設定を調べて、その要求を処理するスレッドのカルチャを Accept-Language に設定されているカルチャに書き換えてくれます。

そして、実行時に、リソースマネージャが現在の要求を処理しているスレッドのカルチャ情報を参照してローカライズされたリソースを検索し、UI に表示されるテキストを取得するという仕組みになっています。

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォルトではシステムのロケールに該当するカルチャがスレッドに設定されます。

なので、ホスティングサービス(Azure も含む)などでサーバーの OS が英語版だったりすると ja フォルダにある日本語のサテライトアセンブリは使われず、元の英語に戻ってしまうはずです。

Tags: ,

Validation

aspx ページでのリソースの利用

by WebSurfer 2015年4月21日 21:22

カスタムコントロールにおけるリソースの使い方は、先の記事 多言語対応カスタムコンロトール に書きました。今回は .aspx ページでリソースを使う場合について書きます。

リソースファイル

ほとんどのことは以下のページ(後者の方が詳しいですが前者のほうが文章が平易で分かりやすいかも)に書いてありますので、わざわざここで書く必要もないかもしれませんが、自分が気がついた注意点などを書いておきます。

Web サイトプロジェクトの場合、リソースファイルは以下の 3 つの場所に配置できます。(Web アプリケーションプロジェクトでは、それに加えてプロジェクト内の任意の場所に置くことができます)

  1. App_GlobalResources フォルダ
  2. App_LocalResources フォルダ
  3. 独立したリソースアセンブリ

上のそれぞれについて、以下に備忘録的なことを書いておきます。

(1) App_GlobalResources フォルダ

Web アプリ全体で利用するためのリソースファイルは、アプリケーションルート直下に App_GlobalResources フォルダを作りその中に配置します。

例として、上の画像のように、MyResource.resx という名前のリソースファイルを配置し、それに Welcome という key 名で文字列を保存することを考えます。(上の画像には BookTitle というのもありますが)

Web アプリケーションプロジェクトでは App_GlobalResources フォルダにリソースファイルを追加すると、xxx.Designer.cs という名前(xxx は拡張子無しのリソースファイル名)の「厳密に型指定されたリソースクラス」が自動的に生成されますが、Web サイトプロジェクトの App_GlobalResources フォルダにリソースファイルを追加した場合はそうならない点に注意してください。

しかしながら、Visual Studio からは見えないものの、見えないところで Resources という名前空間に MyResource という「厳密に型指定されたリソースクラス」が定義されます。

従って、以下の画像のようにコードを入力する際にインテリセンスが働いて、定義済みの候補(画像の例では Resources 名前空間にある MyResource クラス)が表示されます。

インテリセンスによる表示

key 名の Welcome はプロパティとして定義されているので、それに該当するリソースは Resources.MyResource.Welcome というようにして取得できます。

上記の方法の他に、How to: Retrieve Resource Values Programmatically に書かれていますように、GetGlobalResourceObject メソッドでもリソースを取得できます。

下の (2) に書いた HTTP ハンドラのように、一つの共通プログラムでリソース名と key 名を切り替えて画像を取得するような場合は、「厳密に型指定されたリソースクラス」より GetGlobalResourceObject メソッドを使うのがよさそうです。

さらに、上に紹介した URL の記事にあるとおり、.aspx ページに直接 $Resources 式を埋め込んで取得することもできます。文字列を取得するならそれが一番簡単で、本筋だと思われます。

(2) App_LocalResources フォルダ

特定のページで利用するリソースファイルは、そのページのあるフォルダ下に App_LocalResources フォルダを作ってその中に配置します。

リソースファイルの名前の付け方には制約があって、例えば、Default.aspx というページから利用するリソースファイルの名前は Default.aspx.resx となります。

(マイクロソフト公式解説書「プログラミング Microsoft ASP.NET 4」の p.315 によると リソースファイルの名前にフォルダ名をつけると、フォルダレベルで適用可能となるリソースファイルになるとのことです。ただし、自分が試した限りですが、$Resources 式や meta 属性を使って取得できないなど、あまり利用価値はなさそうなのでその話は割愛します)

上の名付けルールにより、特定のリソースファイルを特定の .aspx ページに関連付けているようです。例えば、Default.aspx ページで $Resources 式を使う場合、Text="<% $Resources:Welcome %>" とすれば Default.aspx.resx リソースファイルの中の Welcome という key 名のリソースを取得できます。

(逆に、Text="<% $Resources:Default.aspx,Welcome %>" のようにすると、グローバルリソースを探しに行くらしく、リソースが見つからないというエラーになります)

プログラムでリソースを取得することもできます。その場合、GetLocalResourceObject メソッドを使用します。

自分が試した限りですが、HttpContext クラスの HttpContext.GetLocalResourceObject メソッド を使うと、第 1 引数に .aspx ページの仮想パス(例えば下の画像のようなフォルダ構造で、Default.aspx.resx の中のリソースを取得したい場合は "~/TestFolder/Default.aspx")、第 2 引数にリソースの key 名を指定して、特定のページ以外のどこからでも、どこにあるローカルリソースでも取得できるようです。

ただ、ほとんどのケースでは .aspx ページから直接 $Resource 式や meta 属性を使って取得すれば済むはずで、どのようなケースでプログラムで取得するような必要があるかが疑問です。

自分が思いつくのはリソースが画像の場合ぐらいです。

リソースファイルの画像

例えば、上の画像のように Jpeg などの画像ファイルがリソースに含まれていて、それを ImageButton に表示するというような場合です。

その場合は $Resources 式を使って ImageUrl="<% $Resources:JpegSample %>" のようにしてもダメです。html にレンダリングされると src="System.Drawing.Bitmap" となってしまいます。

ImageUrl プロパティは html ソースでは src 属性になり、それに設定された url をブラウザが Web サーバーに要求に行くので、url 参照で取得できるようにしなければなりません。

それには HTTP ジェネリックハンドラを利用できます。上の画像にある ResourceImageHandler.ashx を以下のように定義して、それを ImageButton の ImageUrl に設定すれば OK です。

<%@ WebHandler Language="C#" Class="_ResourceImageHandler" %>

using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;

public class _ResourceImageHandler : IHttpHandler 
{    
  public void ProcessRequest (HttpContext context) 
  {
    string rsc = context.Request.QueryString["resource"];
    string key = context.Request.QueryString["key"];

    if (!String.IsNullOrEmpty(rsc) && 
        !String.IsNullOrEmpty(key))
    {
      Bitmap bmp = (Bitmap)HttpContext.
          GetLocalResourceObject("~/TestFolder/" + rsc, key);

      if (bmp != null)
      {
        context.Response.
            Cache.SetCacheability(HttpCacheability.Public);
        context.Response.
            Cache.SetExpires(DateTime.Now.AddYears(1));

        context.Response.ContentType = "image/jpeg";
        context.Response.Clear();

        bmp.Save(context.Response.OutputStream, 
                                    ImageFormat.Jpeg);
      }
    }
  }
 
  public bool IsReusable 
  {
    get 
    {
      return false;
    }
  }
}

(3) 独立したリソースアセンブリ

この方法を取ることはまずないと思いますが・・・

Web アプリとは別にプロジェクトを追加し、その中にリソースファイルを配置することも可能です。

例として、ClassLibrary2 という名前で新しいプロジェクトをソリューションに追加し、そのプロジェクトに Resource1.resx という名前のアセンブリリソースファイルを追加することを考えます。

Visual Studio で Resource1.resx を開くと、画面の右上に[アクセス修飾子(I):]がドロップダウン式で選択できるようになっていますが、それを Public にするのを忘れないようにしてください(デフォルトは Internal)。

自動生成される Resource1.Designer.cs ファイルの中に、名前空間名 ClassLibrary2 下に厳密に型指定されたリソースクラス Resource1 が定義されます。

リソースファイルに文字列、テキストファイル、画像などを保存すると、Resource1 クラスに、保存したリソースを取得するためのプロパティが自動的に設定されます。

従って、Web アプリで ClassLibrary2 を参照設定しておけば(そうすると、ソリューションをビルドした時、Web アプリの Bin フォルダに ClassLibrary2.dll が自動的に配置されます)、Web アプリのコードからは ClassLibrary2.Resource1.<プロパティ名> でリソースファイルに保存したリソースを取得できます。

なお、自分の環境で検証した限りですが、GetGlobalResourceObject メソッドや $Resources 式ではリソースを取得できないので注意してください。

-------- 2016/6/17 追記 --------

サテライトアセンブリを追加して多言語対応する場合は Culture, UICulture を "auto" に設定するのを忘れないようにしてください。

Culture, UICulture を "auto" に設定すると、ASP.NET は、ブラウザから送信されてくる要求ヘッダに含まれる Accept-Language の設定を調べて、その要求を処理するスレッドのカルチャを Accept-Language に設定されているカルチャに書き換えます。

そして、リソースマネージャが実行時に、Thread.CurrentUICulture などで得られる CultureInfo(現在の要求を処理しているスレッドのカルチャ情報)を参照してローカライズされたリソースを検索し、UI に表示されるテキストを取得するという仕組みになっています。

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォルトではシステムのロケールに該当するカルチャがスレッドに設定されますので、例えば日本語 OS で xxx.ja-JP.resx というリソースがあれば、常にそれから UI に表示されるテキストを取得します。

Web サイトが日本語専用でサーバーも日本にあれば忘れても問題ないかもしれませんが、ホスティングサービス(Azure も含む)でサーバーが外国にある場合は Culture, UICulture を "auto" に設定するのを忘れると問題が出ると思います。

Tags: , ,

ASP.NET

データアノテーション検証の多言語対応

by WebSurfer 2014年9月11日 20:36

ASP.NET MVC Web アプリケーションのデータアノテーション属性を使用する際、リソースファイル(.resx)を使って表示されるメッセージを多言語化する方法を書きます。

エラーメッセージの多言語化

先の記事 コレクションのデータアノテーション検証 では、モデルのソースコードに中でメッセージをハードコーディングしました。

この記事では、メッセージをリソースファイルに格納し、そこからメッセージを取得して表示します。さらに、日本語と英語のリソースファイルを追加し、ブラウザの言語設定によって言語を切り替える例も紹介します。

さて、まずリソースファイルの格納場所ですが、どこが適当でしょうか?

ASP.NET には App_GlobalResources, App_LocalResources というリソースファイルを格納する専用のフォルダがあります。しかし、そのフォルダにリソースファイルを置くとユニットテストの際に問題があるそうです。(詳しくは Resource Files and ASP.NET MVC Projects の記事を参照)

ということで、この記事では App_GlobalResources フォルダは使わないで、とりあえずアプリケーションルート直下に直接置きました。(フォルダに入れる場合、名前空間にそのフォルダ名が追加になるので注意)

まず、Visual Studio のソリューションエクスプローラーを操作して[新しい項目の追加]ダイアログを表示し、[アセンブリ リソースファイル]を選んでリソースファイルを追加します。下の画像の例ではリソースファイル名を Resources1.resx という名前にしました。そうすると自動生成される Resource1.Designer.cs に定義される「厳密に型指定されたリソースクラス」において Resources1 がクラス名になります。

アセンブリ リソースファイルの追加

リソースファイルを追加したら、下の画像のように、Visual Studio の左側のウィンドウでその内容を設定します。表示するメッセージを[値]欄に、そのメッセージを取得するキー名を[名前]欄に記入します。ここで設定したものがデフォルトのメッセージとなります。

デフォルトのメッセージの設定

上の画像の例では、データアノテーション属性として DisplayAttribute, RequiredAttribute, StringLengthAttribute の 3 つを使用するという前提で、それぞれ Name, Required, StringLength というキー名(名前は任意です)でメッセージを設定しています。上のメッセージは説明用にテキトー書いたものです。実際にはデフォルトにふさわしいメッセージにしてください。

アクセス修飾子の設定を Public にするのを忘れないでください(上の画像で赤枠で囲んだ部分)。デフォルトでは Internal です。Public にするのを忘れると、後でユニットテストをする際に困ることになるはずです。(ちなみに、Internal ⇒ Public に変更すると、プロパティウィンドウで見て、カスタムツールが ResXFileCodeGenerator ⇒ PublicResXFileCodeGenerator に変わります)

Resources1.resx の設定が終わったら、日本語用に Resources1.ja-JP.resx、英語用に Resources1.en-US.resx という名前のリソースファイルを追加します。下の画像は日本語用の Resources1.ja-JP.resx の設定です。英語用も同様に(もちろんメッセージは英語で)設定します。

日本語リソースの設定

ja-JP, en-US というカルチャ名をリソースファイル名に追加するところがミソです。これによってブラウザの言語設定を ASP.NET が検出して(=要求ヘッダ情報を見て)自動的に使用するリソースファイルが切り替わります。

設定が完成すると以下の画像のようなリソースファイルができているはずです。(赤枠で囲った部分)

リソースファイル

次に、モデルでデータアノテーション属性を以下のように設定します。この例では Parent2 クラスの Name プロパティのみにリソースファイルからメッセージを取得して表示するようにしています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace Mvc4App.Models
{
  public class Parent2
  {
    public Parent2()
    {
      Children = new List<Child2>();
    }

    public int Id { get; set; }
        
    [Required(
        ErrorMessageResourceType=typeof(Mvc4App.Resource1),
        ErrorMessageResourceName="Required")]
    [StringLength(
        5, 
        ErrorMessageResourceType = typeof(Mvc4App.Resource1), 
        ErrorMessageResourceName = "StringLength")]
    [Display(
        Name = "Name", 
        ResourceType = typeof(Mvc4App.Resource1))]
    public string Name { get; set; }

    public virtual IList<Child2> Children { get; set; }
  }

  public class Child2
  {
    public int Id { get; set; }
        
    [Required(ErrorMessage = "{0} は必須")]
    [StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
    [Display(Name = "Child Name")]
    public string Name { get; set; }

    public virtual Parent2 Parent { get; set; }
  }
}

RequiredAttribute, StringLengthAttirbute は ErrorMessageResourceType プロパティに Resource1.Designer.cs で定義される「厳密に型指定されたリソースクラス」の型を指定します(この例では typeof(Mvc4App.Resource1) です。Mvc4App が名前空間名、Resource1 がクラス名)。ErrorMessageResourceName プロパティにはリソースファイルに設定したキー名を設定します。

DisplayAttribute は、上の 2 つの属性とは設定するプロパティが異なり、ResourceType プロパティに「厳密に型指定されたリソースクラス」の型、Name プロパティにキー名を設定します。

最後に、web.config で globalization 要素の uiCulture, culture 属性を auto に設定します。これを忘れると、ブラウザの言語指定によるリソースファイルの自動切り替えは行われないので注意してください。(詳しくはこの記事の下の方の「2016/6/17 追記」を見てください)

<system.web>
  <globalization uiCulture="auto" culture="auto" />
</system.web>

これにより、例えば IE の言語の優先順位の設定を、下の画像のように日本語を最優先にしておくと、日本語のリソースファイル Resources1.ja-JP.resx からメッセージを取得して表示します。

IE の言語の優先順位の設定

その結果が一番上の画像です。

-------- 2016/6/17 追記 --------

Culture, UICulture を "auto" に設定すると、ASP.NET は、ブラウザから送信されてくる要求ヘッダに含まれる Accept-Language の設定を調べて、その要求を処理するスレッドのカルチャを Accept-Language に設定されているカルチャに書き換えるようです。

そして、リソースマネージャが実行時に、Thread.CurrentUICulture などで得られる CultureInfo(現在の要求を処理しているスレッドのカルチャ情報)を参照してローカライズされたリソースを検索し、UI に表示されるテキストを取得するという仕組みになっています。

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォルトではシステムのロケールに該当するカルチャがスレッドに設定されますので、例えば日本語 OS で xxx.ja-JP.resx というリソースがあれば、常にそれから UI に表示されるテキストを取得します。

Web サイトが日本語専用でサーバーも日本にあれば忘れても問題ないかもしれませんが、ホスティングサービス(Azure も含む)でサーバーが外国にある場合は Culture, UICulture を "auto" に設定するのを忘れると問題が出ると思います。

ロケール、カルチャ、Culture と UICulture の違いなどについては、記事「カルチャの基本とカルチャ情報」が参考になりましたので、忘れないようにリンクを張っておきます。

Tags: , ,

Validation

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar