エンドユーザーの言語設定を取得する方法はいくつかある。それらの方法で得られるものは何か、どの方法を使えばよいのかを本稿では解説する。
powered by Insider.NET
エンドユーザーがOSに設定している言語を取得したいと思ったことはないだろうか? 例えば、アプリの対応言語に関係なく、エンドユーザーの設定言語に合わせてフォントを切り替えたいといった場合だ。それは簡単そうに思えるが、似たようなAPIが複数あって間違えやすい。そこで本稿では、3つのAPIについて検証し、エンドユーザーの言語設定を取得するのに適したAPIはどれなのか説明する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #92」からダウンロードできる。
ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。
*1 SLAT対応ハードウェアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウェアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。
*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。
*3 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。
*4 マイクロソフトのダウンロードページから誰でも入手できる(このURLはUpdate 3のもの)。
*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションで構わない。Visual Studio Express 2013 with Update 3 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。
本稿では、紛らわしくない限り次の略称を用いる。
Visual Studio 2013 Update 2(およびUpdate 3)では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*6。そのため、本稿で紹介するVBのコードはユニバーサルプロジェクトではなく、PCL(ポータブルクラスライブラリ)を使ったプロジェクトのものである*7。
*6 VB用のユニバーサルプロジェクトは、来年にリリースされるといわれているVisual Studio「14」からの提供となるようだ。「Visual Studio UserVoice」(英語)のリクエストに対する、6月18日付けの「Visual Studio team (Product Team, Microsoft)」からの回答による。
*7 Visual Studio 2013 Update 2(またはUpdate 3)のVBでユニバーサルWindowsアプリを作る場合のお勧めは、「The Visual Basic Team」のブログ記事(英語)によれば、PCLを使う方法のようである。PCLに置いたものは、コードだけでなくXAML(画面)やリソースディクショナリなども共通に利用できる。そこで別途公開のサンプルコードでも、VBはWindows用/Phone用/共通コード(PCL)の3プロジェクト構成とした。ユニバーサルプロジェクトで作らなくてもユニバーサルWindowsアプリはリリースできるのである(「WinRT/Metro TIPS:ユニバーサルプロジェクトで開発するには?」参照)。
ストアアプリで言語設定を取得するAPIには、主なものとして以下に示す3つがある(他にも、例えばWin32 APIを使うなどの方法がある)。なお、ここでカギかっこを付けた説明は、MSDNからの引用である。
これら3つのAPIの差異は、MSDNの説明を読んでもよく分からないのではないだろうか。実際に使ってみて、その違いを検証してみよう。
*8 以前から.NET Frameworkに親しんできている開発者のために補足しておく。CultureInfo.CurrentUICultureプロパティの仕様が、.NET Framework 4.5で変更されているので注意してほしい。英語のMSDNを見ると、.NET Framework 4.5では次のように説明されている。
「When a thread is started, its UI culture is initially determined as follows:
・ By retrieving the culture that is specified by the DefaultThreadCurrentUICulture property in the application domain in which the thread is executing, if the property value is not null.
・ By calling the Windows GetUserDefaultUILanguage function.」
この説明によると、CurrentUICultureは2段階のステップで決定される。第1ステップで決定できなかったときに、第2ステップ(=Win32 APIのGetUserDefaultUILanguage関数呼び出し)が使われるのだ。従来(.NET Framework 4まで)は、第1ステップがなく、第2ステップのみであった。
それでは、3つのAPIを利用するサンプルアプリを作ってみよう。手順の説明はC#でさせていただく(VBは別途公開のサンプルコードをご覧いただきたい)。
ユニバーサルプロジェクトを新規に作成したら、日本語と英語の多言語化対応アプリにする。多言語化対応していないと、CultureInfo.CurrentUICultureプロパティとApplicationLanguages.Languagesプロパティの値がまったく同じになってしまい、両者の違いが分からないのだ。次の画像のように、アプリの既定の言語を「en-US」に変更し、日本語と英語のリソースを作成する。
リソースファイルには最低1つの文字列リソースを言語ごとに違えて定義し、「MainPage.xaml」ファイルなどに表示されるようにしておく(次の画像)。これで、実行時にどの言語の文字列リソースを使用しているかが分かるようになる。
次に、共有プロジェクトにユーザーコントロールを追加し、取得した言語を表示するためのUIを作る(次の画像)。
このユーザーコントロールのコードビハインドに、言語を取得して表示するコードを記述する(次のコード)。
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// ユーザーコントロールがロードされたときに、言語を取得して画面に表示する
LoadLanguages();
}
// 言語を取得して画面に表示する
private void LoadLanguages()
{
// CultureInfo.CurrentUICultureプロパティ
// そのNameプロパティが言語を表す「en-US」といった文字列になっている
{
// 得られた言語を表示する
this.CultureInfoText.Text
= System.Globalization.CultureInfo.CurrentUICulture.Name;
}
// ApplicationLanguages.Languagesプロパティ
// 言語を表す「en-US」といった文字列の読み取り専用リスト
{
IReadOnlyList<string> appLanguages
= Windows.Globalization.ApplicationLanguages.Languages;
// 得られた言語を全て表示する
var buff = new System.Text.StringBuilder();
int index = 0;
foreach (var lang in appLanguages)
buff.AppendLine(string.Format("[{0}] {1}", index++, lang));
this.ApplicationLanguagesTextBlock.Text = buff.ToString();
}
// GlobalizationPreferences.Languagesプロパティ
// 言語を表す「en-US」といった文字列の読み取り専用リスト
{
IReadOnlyList<string> prefLanguages
= Windows.System.UserProfile.GlobalizationPreferences.Languages;
// 得られた言語を全て表示する
var buff = new System.Text.StringBuilder();
int index = 0;
foreach (var lang in prefLanguages)
buff.AppendLine(string.Format("[{0}] {1}", index++, lang));
this.GlobalizationPreferencesTextBlock.Text = buff.ToString();
// GlobalizationPreferences.Languagesプロパティの最初の言語を、
// サンプルテキストのLanguageプロパティにセットする
this.SampleText.Language = prefLanguages[0];
}
}
Private Sub UserControl_Loaded(sender As Object, e As RoutedEventArgs)
' ユーザーコントロールがロードされたときに、言語を取得して画面に表示する
LoadLanguages()
End Sub
' 言語を取得して画面に表示する
Private Sub LoadLanguages()
' CultureInfo.CurrentUICultureプロパティ
' そのNameプロパティが言語を表す「en-US」といった文字列になっている
' 得られた言語を表示する
Me.CultureInfoText.Text _
= System.Globalization.CultureInfo.CurrentUICulture.Name
' ApplicationLanguages.Languagesプロパティ
' 言語を表す「en-US」といった文字列の読み取り専用リスト
Dim appLanguages As IReadOnlyList(Of String) _
= Windows.Globalization.ApplicationLanguages.Languages
' 得られた言語を全て表示する
Dim buff1 = New System.Text.StringBuilder()
Dim index1 As Integer = 0
For Each lang In appLanguages
buff1.AppendLine(String.Format("[{0}] {1}", index1, lang)) : index1 += 1
Me.ApplicationLanguagesTextBlock.Text = buff1.ToString()
Next
' GlobalizationPreferences.Languagesプロパティ
' 言語を表す「en-US」といった文字列の読み取り専用リスト
Dim prefLanguages As IReadOnlyList(Of String) _
= Windows.System.UserProfile.GlobalizationPreferences.Languages
' 得られた言語を全て表示する
Dim buff2 = New System.Text.StringBuilder()
Dim index2 As Integer = 0
For Each lang In prefLanguages
buff2.AppendLine(String.Format("[{0}] {1}", index2, lang)) : index2 += 1
Next
Me.GlobalizationPreferencesTextBlock.Text = buff2.ToString()
' GlobalizationPreferences.Languagesプロパティの最初の言語を、
' サンプルテキストのLanguageプロパティにセットする
Me.SampleText.Language = prefLanguages(0)
End Sub
このユーザーコントロールをWindowsとPhoneのそれぞれの「MainPage.xaml」画面に貼り付ければ、検証用のアプリは完成だ。
上で作ったアプリを使って、3つのAPIの違いを検証しよう。
検証に当たって、OSの言語設定を次の画像のように変更する(検証が終わったら元に戻してほしい)。優先順位の高い方から、中国語/日本語/英語/ドイツ語とする。本稿の目的は、この一覧の内容を取得する(少なくとも最優先になっている中国語を取得する)ことである。
それではアプリを実行してみよう。次の画像のような結果になる。
3つのAPIのうちGlobalizationPreferences.Languagesプロパティだけが、エンドユーザーが設定している言語をその優先順位も含めて全て取得できている。
従って、本稿の結論はこうだ。エンドユーザーの言語設定を取得するには、GlobalizationPreferences.Languagesプロパティを使えばよい。
いくつか補足しておこう。
まず、3つのAPIの違いについてだ。GlobalizationPreferences.Languagesプロパティは、上で検証したようにエンドユーザーの設定言語のリストが得られた。残りの2つは、何を取得しているのだろうか?
CultureInfo.CurrentUICultureプロパティは、動作中のアプリの言語だ。これは、実行時に適用されている言語リソースと一致する。上の検証結果の画像で、タイトルの右に表示された言語(これは文字列リソースから取得するように作成した)も、CultureInfo.CurrentUICultureプロパティの結果も、どちらも日本語(ja)になっていることから確認できる。また、CultureInfo.CurrentUICultureプロパティの値は、アプリが対応している言語のいずれかになる。この検証用アプリの場合は、必ず日本語か英語のどちらかになる。
ApplicationLanguages.Languagesプロパティは、エンドユーザーの設定言語のリストの中から、アプリの対応言語を抜き出したリストだ(ただし、設定言語中にマッチする言語がないときはCultureInfo.CurrentUICultureプロパティと同じになる)。その順序は元のリストの順序関係が維持される。上の検証例では、エンドユーザーの設定言語は優先度の高い順に中国語/日本語/英語/ドイツ語となっており、その中からアプリの対応言語にマッチした日本語/英語が得られている(アプリの既定の言語が上位に来るわけではない)。ApplicationLanguages.Languagesプロパティの先頭に来る言語は、CultureInfo.CurrentUICultureプロパティの言語と一致する。
ところで、上の検証例ではサンプルテキストのLanguageプロパティに中国語が設定されたため、中国語のフォントが表示されていた。エンドユーザーの設定言語の先頭が日本語なら、日本語のフォントが表示されるだろう。では、それ以外の言語がエンドユーザーの設定言語の先頭にあったらどうなるだろうか? OSの言語設定をドイツ語/英語/中国語/日本語の順に変えた場合は、次の画像のような結果になった。Windowsでは日本語のフォントで、Phoneでは中国語のフォントで表示される*9。
*9 これが日本で俗にいうPhoneの「中華フォント問題」である。しかしこれは、中国におけるWindowsの「日本語フォント問題」でもあるのだ。Languageプロパティの既定値は「en-US」であるため、Languageプロパティに何も指定しなければWindowsでは日本語のフォント、Phoneでは中国語のフォントで表示されるのである。対処するには、適切な言語をLanguageプロパティに設定するか、あるいは適切なフォントを直接指定することになる(適切な言語やフォントの決定方法はケースバイケースだ)。ちなみに、今回のPhoneアプリのページタイトル(=「言語設定を取得する」)には、「Yu Gothic」フォントを直接指定してある。
エンドユーザーがOSに設定している言語を取得するには、GlobalizationPreferences.Languagesプロパティを使う。OSの設定言語とアプリの対応言語の組み合わせからシステムが決定した実行中のアプリの言語は、CultureInfo.CurrentUICultureプロパティかApplicationLanguages.Languagesプロパティで取得できる。
Copyright© Digital Advantage Corp. All Rights Reserved.