XAML のマークアップ拡張を使った WPF での国際化
今回は XAML のマークアップ拡張機能を利用して、 WPF で国際化をする方法についての記事です。
前に Resx のリソースファイルと StaticResource を使った国際化について書きました。 そちらの方法で国際化はできるのですが、一つ問題があります。 .NET 自体のバグだと思うのですが、最初のビルドやリビルドで作成した実行ファイルでは XamlParseException という実行時例外のエラーが発生してしまう点です。これはソースファイルなどをちょこっと変えてビルドするとエラーはでなくなります。 ただ、少し不便ですし、ビルドサーバーでのビルドといった場合に困ります。
今回の方法は Resx ファイルのリソースを使うところは同じで、 国際化した文字列を取得するのにマークアップ拡張を使うようにします。
XAML に直接記述していくことになるので、はっきり言って、こちらの方がめんどくさいです。 その代わり、エラーはでなくなります。
なお、今回の方法に関しては以下のサイトを参考にさせていただきました。
プロジェクトとリソースの準備
サンプルを作りながら、国際化の方法を説明していきます。 前の回と同じところは詳しい説明は省略しているので、そちらの記事をご覧ください。まず、プロジェクトを作成して以下のような画面を作成します。 ここではまだボタンとタイトルの文字設定はやらずにそのままにしておきます。

次に Resource.resx, Resource.ja-JP.resx の 2 つのリソースを作成します。
リソースを利用するための App.xaml への修正も今回は必要ありません。
Resources.resx :

Resources.ja-JP.resx :

マークアップ拡張
XAML にはマークアップ拡張という機能があり、 XAML の記述を拡張することができます。これは {{StaticResource xxxx}} のような記法で、 StaticResource や Binding に使われています。 このマークアップ拡張は自分でも追加することができます。これを利用して国際化します。
拡張定義の追加
追加は MarkupExtension を継承したクラスで行います。クラスの追加で、以下のファイルをプロジェクトに追加します。
TrExtension.cs :
[MarkupExtensionReturnTypeAttribute(typeof(string))] public class TrExtension : MarkupExtension { string _key; public TrExtension(string key) { _key = key; } const string NotFoundError = "#StringNotFound#"; public override object ProvideValue(IServiceProvider serviceProvider) { if (string.IsNullOrEmpty(_key)) return NotFoundError; return Properties.Resources.ResourceManager.GetString(_key) ?? NotFoundError; } }クラス名の Extension の前(Tr)が XAML での呼び出し用の名前です。
マークアップ拡張による国際化は例外が発生しない以外にも利点があります。 今回は resx のリソースを使って国際化していますが、
ProvideValue()
のメソッドの中身を変えることによって、
独自の方法で国際化するといったことも可能になります。
XAML での記述
拡張した定義を使う場合には各 XAML ファイルで次のような記述を行います。MainWindow.xaml :
<Window x:Class="MarkupI18nSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:my="clr-namespace:MarkupI18nSample" Title="{my:Tr UiWindowTitle}" Height="171" Width="338"> <Grid> <Button Content="{my:Tr UiButtonGreet}" Height="23" HorizontalAlignment="Left" Margin="117,52,0,0" Name="buttonGreet" VerticalAlignment="Top" Width="75" Click="buttonGreet_Click" /> </Grid> </Window>記述のスタイルは
{{my:Tr キーワード}}
の形式です。
my でネームスペースを表すようにしています。これでコンパイルすれば、デザイン用の画面でも国際化されて表示されているはずです。
サンプルコード
XAML 外のコード中でのリソースの利用法や言語切り替え用のコードなどは前回と同じです。それらのコードも追加したサンプルコードもあげておきます。
その他の国際化方法
WPF での国際化について今回少し調べて見ました。おそらく国際化に関しては以下のサイトが一番詳しいと思います。
WPF での国際化の方法は 3 通りほどあるようです。
- resx ファイルを使った方法
- マークアップ拡張を使った方法 (今回)
- LocBaml を使った方法
やり方も csproj を直接編集する必要があったり、 LocBaml も使い勝手が悪い上にちゃんと更新もされていなかったり と Microsoft が本気でこの方法を勧めているのか疑問です。
WPF の国際化はどれも問題があり、 これ といった方法がありません。
個人的には resx を使用した方法がいいと思うのですが、 バグが解消されるまでは、今回のマークアップ拡張を利用した方法も使い分けていく必要があるかなと思います。
また、有料のツールを使ってもよいということであれば、 Sisulizer というツールがよく使われているようです。
RedScript : Ruby 風の JavaScript 変換言語
今回は JavaScript 変換して使う Ruby っぽい RedScript について紹介します。
RedScript とは
RedScript は Ruby 風の言語で、 JavaScript に変換して使います。 変換後の JavaScript のコードが読みやすいといった特徴を持っています。という風に説明すると "それってもう CoffeeScript があるじゃん" と思う人もいるかもしれません。
確かにそのとおりなのですが、 RedScript は CoffeeScript よりも、もっと Ruby よりです。 Ruby を知っていれば、かなり学習コストの低い言語になっていると思います。 Python 由来のあのインデントブロックなんかもありません。
位置づけとしては CoffeeScript のような JavaScript の軽いラッパーにあたるのではないかと思います。
これは別にラッパーだから劣っているというような意味ではないです。ラッパーにはラッパーの良さがあります。 Ruby から JavaScript からの変換というと Opal というツールもあります。
こちらは逆にガッツリと Ruby で、 ラッパー感がなくなり、 JavaScript への変換を意識したコーディングというものができなくなります。
RedScript はその辺の間を狙った言語ではないかなと思います。
また、言語のホームページによれば、 クライアントサイドの JavaScript というよりも Node.js で使うコードとして使うことをメインに想定しているようです。
RedScript の特徴
RedScript のサイトには、多くのサンプルコードが変換後の JavaScript と一緒に載せられています。 Ruby と JavaScript の知識があれば、眺めているだけでなんとなく書き方は分かるのではないでしょうか。ここでは、その中からいくつか抜粋して紹介したいと思います。
静的クラスでの定義や継承ができます。
ただ、 end の後の , や ) はちょっと気持ち悪いです。
class Animal def init(name) @name = name end, def sayHi puts "Hello" end end) class Duck < Animal def init super name alert("#{@name} is alive!") end, def sayHi super puts "Quack" end end) duck = new Duck('Darick') duck.sayHi()クラスではなく、 オブジェクトを直接作ることもできます。
プロトタイプへのメソッドの追加用に >> なども用意されています。
object auto wheels: 4, engine: true, def honk puts "hoooonk" end, def sayHi(msg) puts msg end end # object ブロック外での定義 def auto.add(x, y) return x + @wheels end # プロトタイプへのメソッド追加 def Car >> sub(x,y) return x - y end無名関数を渡す場合に do ... end ブロックを使うことができます。
getUser('/users/1', do alert(user) end)
インストール
Windows でのインストール方法について説明します。 Node.js の npm を使うので、他の環境でもあまり変わらないと思います。Node.js 上で動作するので、先にインストールしておく必要があります。 Node.js のインストール方法については以下の記事を見て下さい。 npm を使ってインストールします。 コマンドとして使うので、 -g が必要です。
~/lang/rs $ npm install -g redscript
これでインストールは完了です。
redscript(.cmd) で動作します。使い方
コマンド引数にソースファイルを指定して実行すると、拡張子を js にした JavaScript ファイルが生成されます。ただ、出力に制御コードを使っているらしく、対応していないターミナルだと変な文字が出ます。
~/lang/rs $ redscript test.rs 笨\224 Writing test.js引数なしで実行すると Usage が表示されます。
~/lang/rs $ redscript
No RedScript files specified, type --help for more info
redscript [filename1] [filename2]
redscript watch [filename1] [filename2]
"--help" で詳しい情報と出てますが、ヘルプはまだ実装されていないらしく、エラーとなります。
感想
ちょっと使ってみたところバグがボロボロと出てきました。(Ver. 0.1.0)ただ、そこはまだ開発段階みたいなので、今後は直っていくのではないかと思います。
ブロックの表現に関して、私はブレース {} と end が適度に混ぜられる Ruby が一番読みやすいと思っています。 ブレースばっかり、 end ばっかりは読みづらいです。インデントだけなんてのは論外でしょう。
RedScript も多少 ブレース {} と end の組み合わせはできます。 ただ、 end の後に対応のない
)
とかが気持ち悪く、
ここは改良して欲しいところです。また、 Ruby の優れている点の一つとしてエレガントなコードブロックがあります。
関数の引数に無名関数を渡すところは do ... end を差し込むのではなくて、 もっと Ruby っぽいコードブロックになるといいのになと思います。
RedScript はまだまだ問題点の多い言語ですが、 Ruby っぽく JavaScript が書けるのは魅力的です。 CoffeeScript にとって代わるくらいに頑張ってほしいです。
静的型付けでの型推論と動的型付けでの型チェック
最近の言語では 「静的型付けは型推論」、「動的型付けは型チェック」 と同じようなところに向かっている傾向がみられます。 今回は最近のプログラミング言語におけるこれらの型システムについて書いてみたいと思います。
静的型付けと動的型付け
まず、静的型付けと動的型付けについて簡単に説明します。静的型付けでは 変数を宣言するときに型も指定します。 整数用の変数には整数のみ、文字列用の変数には文字列のみと、 一つの変数には一つの型しか入れることができません。
int foo; string bar; bar = 3; // エラー一方、 動的型付け では 変数に任意の型を入れられるようになっています。
var foo; foo = 1; foo = "Hello"; var bar; // 未初期化の場合は null や undefined などの値
動的型付けのメリットは、その手軽さです。
自由度が高く、後述するダックタイピングもできます。これにより、配列などのコンテナーに任意の型を格納することも可能になります。
Ruby 、 JavaScript をはじめとしたスクリプト言語の多くは動的型付けが採用されています。
静的型付けのメリットはというと速度です。
動的な型では自由度の代償として、高速なコンパイルコードの生成が難しくなります。 静的な型でなければ、コンパイルできないということではありませんが、 C++ のようなコンパイル型の言語では速度を求められることが多いので、たいてい静的型付けです。
Java, C# のような仮想マシン(VM)の言語も静的型付けが多いです。 VM では実行時(JIT)コンパイルを使って高速化されることが多く、 それには静的型付けがよいためです。
ただ、最近では動的型付けでも JIT コンパイルも高速化はされてきています。 なお、 動的型付けはスクリプト(インタプリター)型言語、静的型付けはコンパイル型言語というのは、 そういうったものが多いというだけで、例外もあります。
静的型付けにおける型推論
動的型付けの方が楽ですが、速度を求められる場合は静的型付けにする必要があります。でも、やっぱり面倒だから、コンパイラーに頑張らせて、省略できるところは省略しようというのが型推論です。
例えば、次のような C++ のコードでは、初期化しているので、型が何かコンパイラーは分かります。 型推論の機能が言語にあれば、こういう時に省略できます。
int foo = 3; string bar = "Hello";Scala、 C# など最近の静的型付け言語では型推論に対応しているのが普通になってきました。
なお、型推論は動的型付けに似ていますが、別物です。 JavaScript と型推論用の C# の変数宣言は同じ
var
を使いますが、違いがあります。型推論 :
var foo = 3; // 整数型で定義したのと同じ foo = "Hello"; // foo は整数型なのでエラー var bar; // 初期化しないと特定できないのでエラー
動的型付けにおける型チェック
動的型付けにはコードの安全性という面で欠点があります。例えば、次のような Ruby のメソッド(関数)定義があったとします。
def foo(num) # ... bar = num + 1 # 数値ではないとエラーになる # ... endこの場合、動的型付けに起因する次の問題があります。 これはライブラリーなどの場合に特に顕著になります。
- メソッドの内部でエラーが発生する
- 中身までみたくない、エラー原因を特定しづらい
- メソッドの引数の型をコメントで書く必要がある
- コメントに頼らないコードがよいコード
PHP(Hack) や Dart, TypeScript といった JavaScript の改良言語などです。
int foo(int bar) // 引数、戻り値で型指定 { // ... } foo("Hello"); // foo() を使用するときに型チェックされるまた、引数の型指定ができるのであれば、関数のオーバーロードもできるようになります。 この機能があれば、より使いやすい言語になると思います。
これらの理由により、もともと型指定のある静的言語ではローカル変数だけ型推論で省略し、 型指定のない動的言語では関数宣言のところだけ型指定するという傾向ができています。
ダックタイピングとインターフェース
関数で引数の型指定をしないのは悪いことのように書きましたが、 メリットもあります。 それはダックタイピングできることです。ダックタイピングとは "アヒルのように振舞えば、それはアヒル" という言葉に由来しています。
例えば、 Ruby で次のような構文解析用のメソッドがあったとします。
def parse(fp) # ... while (str = fp.gets()) # 一行読み取る処理 # ... end # ... endこの場合、渡す引数 fp には一行読み取る gets() というメソッドを持っているかだけ が重要となります。 gets() さえ実装されていれば、文字列でもファイルでも使えますし、 圧縮ファイルやネットワーク通信を仮想化したものでも構いません。
ただし、これはいいことばかりでなく、先ほどの型チェックと同じ問題があります。 使用可能なものをコメントで説明する必要があったり、変数のクラスが必要なメソッドを実装しているか保障されていなかったりします。
この保障を与えるのがインターフェースです。 これは Java, C# といった言語にある機能で、 C++ でも純粋仮想関数を使って作ることができます。 また、動的型付けでも Dart のように型チェック機能がある言語ではインタフェースを持つものもあります。
しかし、言語にインタフェースの機能があったとしても、インターフェースが常に使えるわけではありません。
例えば、先ほどのファイルオブジェクトの場合などは標準クラスのものが多く、 後でインターフェースを継承させるといったことは難しいでしょう。
オプショナルな型チェックと自動ジェネリック
ダックタイピングしたい場合もあれば、インターフェースなどで型指定したい場合もあります。動的型付けでは型チェック機能があった方がいいと書きましたが、 ベストはオプショナルな型チェックです。 これは型指定していれば型チェックし、無ければダックタイピングとなります。
ただ、動的言語の場合には、もともと型チェックしていないところに追加しているので、 オプショナルにするのはそれほど難しくなく、型チェックはオプショナルになっているのが普通です。
int foo(int bar) // 型チェックあり { // ... } foo(bar) // 型チェックなし { // ... }静的型付けでも両方できるのが理想です。
ただし、静的型付けは本来型指定するものなので、 C++ のテンプレート(ジェネリック)のようなダックタイピングを実現する機能が必要となります。 こういったジェネリックの機能を持つ静的型付け言語は結構あるのですが、 C++ のテンプレートのように書くのが面倒なものが多いです。
しかし、最近では F#(OCaml) のように型指定を省略すれば、自動的にジェネリックになる(自動ジェネリック化)機能を持った言語も増えてきました。
// 型指定を省略するだけでジェネリック let max a b = if a > b then a else b let biggestInt = max 2 3 let testString = max "cab" "cat"
JavaScript 変換言語における型システム
型システムに関して、以前の記事の補足的な話もしておきます。 静的型付けのメリットは速度と書きましたが、これが当てはまらない分野の言語があります。 それは altJS などと呼ばれる JavaScript に変換して使う言語です。 これらの言語ではJavaScript に変換されるので、動的型付けに戻ってしまうためです。以前の記事では、型指定の機能は 一緒くた にしていましたが、実は中身は微妙に違います。
Dart や TypeScript は関数のオプショナルな型指定であり、 これは純粋に利点です。
しかし、 Haxe や JSX は静的型付けになっています。 これは JavaScript コードの生成だけを見れば、やりすぎと言ってよいでしょう。 生成する JavaScript コードの最適化に多少は役に立っているのかもしれませんが、 大きな速度のメリットもなく、手軽さが失われています。
この 2 つの言語は JavaScript 以外の言語への生成も考慮されており、 そのための仕方ない処置とも言えます。 また、型推論もあるので、そんなに気になるほどの欠点でもないです。
ちなみに Dart に関しては、最終的には JavaScript に置き換えて、 Dart のまま動作させるので、静的型付けによる速度のメリットはあります。 ただ、 Dart は他のスクリプト言語同様に手軽さのためか動的形付けが採用されています。
また、実際には Dart は関数宣言だけでなく、ローカル変数でも型指定できますが、 スタイルガイドで指定しない方を推薦されています。
まとめ
いろいろ書いてきましたが、静的型付け、動的型付け、どちらがいいという問題ではありません。静的型付けは安心感を与えるので、静的型付け派の人もいますが、 個人的には適材適所で、スクリプト言語であれば動的型付け、コンパイル言語では静的型付けでよいと思っています。
ただ、静的型付け、動的型付けのそれぞれともメリット、デメリットがあります。 このデメリットを軽減させる機能が型推論、型チェックです。
静的型付けでは型推論に加えて、自動ジェネリックの機能もあれば、型指定に関してスクリプトとほぼ同じ書きごごちが得られます。
なお、"型推論や自動ジェネリックで手軽さが得られるのであれば、スクリプト言語でも静的型付けにして速度向上できるのではないか" と思った人もいるかもしれません。 しかし、これらの機能は、ともにコード解析に結構負担をかける機能です。 コード解析に時間をかけられないスクリプト言語では難しい面があります
動的型付けでは型チェック、特にオプショナルな型チェックがあれば、 手軽さとダックタイピングのよさを残したまま、コードの安全性が向上します。 さらに関数のオーバーロードもできれば、より良いでしょう。
これら型推論、型チェックなどの機能を持つ言語が増えてきてます。今後もそれが進んで欲しいと思います。