ローカライズって面倒ですよね。
ASP.NET MVC 3 の Razor でも多言語対応を試してみる - しばやん雑記 でリソースを使った Razor でのローカライズ方法を書きましたが、App_GlobalResources と App_LocalResources というディレクトリを作るのは面倒です。特に App_LocalResources をビューのディレクトリごとに作らないといけないとか苦痛です。
折角 ASP.NET MVC では CoC が使われているので、リソースも規約でいろいろ出来たらいいなと思ってたら出来ました。ディレクトリ構成は以下のような感じになってます。
みにもば用に書いていたので
Minimoba/Resources/Views/Shared/_Layout.resx
というパスになっています。名前空間も直接対応しているので
Minimoba.Resources.Views.Shared._Layout
という形です。Resources 以下が Views の構成と一致していることが分かって貰えると思います。単純に言えば、ビューの仮想パスからこの形のパスを作るだけです。パスを作ってしまえば ResourceManager に突っ込めばあとはお任せです。
しかし、今回は HTML ヘルパーという形を選択したのでビューの仮想パスを取得するのに迷いました。仮想パスを取得するために Razor 限定になってしまったのですが、詳しくはソース読んでください。
追記
@shibayan Resource(this HtmlHelper helper, → Resource(this TemplateFileInfo info, とかどうだろ? 使う時 Html.Resouce() じゃなくて TemplateInfo.Resource() に
— K. Takaoka (@ReadyBUG) 2011, 7月 26
ついったーで TemplateInfo を使った方がいいよと指摘を受けたので修正しました。TemplateFileInfo には VirtualPath というそのまんまのプロパティがあったんですね・・・。知りませんでした。
public static class ResourceExtensions { private static readonly string RootNamespace = typeof(MvcApplication).Namespace; private static readonly Dictionary<string, ResourceManager> ResourceManagers = new Dictionary<string, ResourceManager>(); public static string Resource(this TemplateFileInfo info, string name, params object[] args) { // ビューの仮想パスから resx のパスを作成する var resourcePath = GetResourcePath(info.VirtualPath); if (resourcePath == null) { return null; } ResourceManager resourceManager; // 既にリソースマネージャを作成しているか確認する if (!ResourceManagers.TryGetValue(resourcePath, out resourceManager)) { resourceManager = new ResourceManager(resourcePath, Assembly.GetExecutingAssembly()); ResourceManagers.Add(resourcePath, resourceManager); } // リソースから文字列を取得して、フォーマットして返す return string.Format(resourceManager.GetString(name, Thread.CurrentThread.CurrentUICulture), args); } private static string GetResourcePath(string virtualPath) { if (virtualPath == null) { return null; } // ~/Views/Shared/_Layout.cshtml のような文字列から ~/ と .cshtml を削除する var tokens = virtualPath.Substring(2, virtualPath.Length - 9).Split('/'); if (tokens.Length == 0) { return null; } var textInfo = CultureInfo.InvariantCulture.TextInfo; // 先頭文字を大文字にしつつ、リソースのパスを作成 return string.Format("{0}.Resources.{1}", RootNamespace, string.Join(".", tokens.Select(textInfo.ToTitleCase))); } }
普通なら WebViewPage を継承したクラス作るべきなんでしょうけど、あまりやりたくなかったので HTML ヘルパーです。コピペするだけでたぶん使えます。
使い方はいつも通りのヘルパーなので正直説明要らないですよね。とりあえず書いておきます。
@* リソースから取得 *@ <h1>@TemplateInfo.Resource("TitleString")</h1> @* フォーマットして取得 *@ <h2>@TemplateInfo.Resource("FormatString", 100, 3.14)</h2>
ちゃんと WebViewPage から仮想パスを取得しているので、レイアウトを指定している場合でも正しいリソースを参照することが出来ます。自分的には納得できるものが出来たので、ようやくみにもばのローカライズが進みそうです。