Windows Phone アプリのUIレイアウトXAMLから文字列リソースを参照する

普通アプリを書く時は端末の言語設定によってテキストリソースを切り替えられるようにする訳ですが、Windows Phone 7.1 SDK でそれをやるのに多少苦労したのでメモしておきます。

もっといい方法を御存知の方は是非教えて下さい。

方法A

xamlマークアップ拡張には多国語化リソースを切り替えて参照する機能はありませんが、Binding マークアップ拡張を使うと同じ事を実現できます。

マークアップ拡張の中でリソース名称の文字列をLabelConverterに渡す際、この例ではバインディングプロパティ ConverterParameter を経由しています。奇妙に思えるかもしれませんが、ConverterParameter は引数を単なる文字列として評価できるのに対して、Source, ElementName, RelativeSource などでは他の属性やStaticResourceを経由しないと文字列を渡すことができないため煩雑になってしまいます。

ただし全くデータソースを指定しないとConverterが呼び出されないので、この例ではダミーのStaticResource (文字列 "_") を使用しています。

文字列リソースの用意

この例では"Resources"フォルダの下にLabels.resx を作ります。

App.xamlルート要素の属性に、以下の名前空間を追加
<Application 
(snip)
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:resources="clr-namespace:FooBarApp.Resources"
>
App.xamlのルート要素の下にを追加
    <Application.Resources>
    	(snip)
        <sys:String x:Key="_"></sys:String>
        <resources:LabelConverter x:Key="Labels"/>
    </Application.Resources>
Resourcesフォルダの下にクラスファイル LabelConverter.cs を追加
namespace FooBarApp.Resources {
    public class LabelConverter  : IValueConverter{
        private static ResourceManager resman = new ResourceManager("FooBarApp.Resources.Labels", typeof(Labels).Assembly);

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){
            if( parameter == null ){
                throw new NullReferenceException( "LabelConverter: resource id is not specified as ConverterParameter" );
            }
            var key = parameter.ToString();
            try {
                return resman.GetString( parameter.ToString(), culture );
            }catch(Exception){
            }
            return parameter;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture){
            return "";
        }
    }
}
UIレイアウトのxaml に {Binding} マークアップ拡張を書く
<TextBlock Text="{Binding Source=_, Converter={StaticResource Labels}, ConverterParameter=text1 }" />

方法B

How to: Build a Localized Application for Windows Phone で説明されていた方法です。
方法Aより少しだけ短くなります。

方法Aとの違いはConverterではなくSourceに自作クラスを指定することと、Pathにそのクラスのメンバを指定することです。この場合でもやはりダミーの識別子が1個残ります。

文字列リソースの用意

方法Aと同じ。
ただしリソース内容を編集するウィンドウで「アクセス修飾子」をPublicにしておく必要があります。

App.xamlのルート要素の属性に、以下の名前空間を追加
<Application 
(snip)
    xmlns:resources="clr-namespace:FooBarApp.Resources"
>
App.xamlのルート要素の下にを追加
    <Application.Resources>
    	(snip)
       <resources:LabelConverter x:Key="Labels" />
    </Application.Resources>
Resourcesフォルダの下にクラスファイル LabelConverter.cs を追加
namespace FooBarApp.Resources {
    public class LabelConverter {
        private static Labels labels = new Labels();

        public Labels _ { get { return labels; } }
    }
}
UIレイアウトのxaml に {Binding} マークアップ拡張を書く
<TextBlock Text="{Binding Source={StaticResource Labels}, Path=_.text1}" />

感想

この方法を使うと、XAMLから参照したテキストを実行時に端末の言語設定にあわせて変化させることができるようになります。

しかし、androidのUIレイアウトが"@string/resource_name" で済むのと比べて、ムダに複雑になっている気がします。もっと単純な方法はないんでしょうか…。