エンドユーザーの言語設定を取得するには?[ユニバーサルWindowsアプリ開発]WinRT/Metro TIPS

エンドユーザーの言語設定を取得する方法はいくつかある。それらの方法で得られるものは何か、どの方法を使えばよいのかを本稿では解説する。

» 2014年10月23日 13時21分 公開
[山本康彦BluewaterSoft/Microsoft MVP for Windows Platform Development]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 エンドユーザーがOSに設定している言語を取得したいと思ったことはないだろうか? 例えば、アプリの対応言語に関係なく、エンドユーザーの設定言語に合わせてフォントを切り替えたいといった場合だ。それは簡単そうに思えるが、似たようなAPIが複数あって間違えやすい。そこで本稿では、3つのAPIについて検証し、エンドユーザーの言語設定を取得するのに適したAPIはどれなのか説明する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #92」からダウンロードできる。

事前準備

 ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。

  • SLAT対応のPC*1
  • 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上*3
  • Visual Studio 2013 Update 2(またはそれ以降)*4を適用済みのVisual Studio 2013(以降、VS 2013)*5

*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」はデスクトップで動作するアプリ用)。


用語

 本稿では、紛らわしくない限り次の略称を用いる。

  • Windows:Windows 8.1とWindows RT 8.1(2014年4月のアップデートを適用済みのもの)
  • Phone:Windows Phone 8.1

サンプルコードについて

 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:ユニバーサルプロジェクトで開発するには?」参照)。


3つのAPI

 ストアアプリで言語設定を取得するAPIには、主なものとして以下に示す3つがある(他にも、例えばWin32 APIを使うなどの方法がある)。なお、ここでカギかっこを付けた説明は、MSDNからの引用である。

  • CultureInfo.CurrentUICultureプロパティ:CultureInfoクラス(System.Globalization名前空間)のCurrentUICultureプロパティ。「実行時にカルチャ固有のリソースを参照するためにリソース マネージャーによって使用される現在のユーザー インターフェイスのカルチャを表す」*8
  • ApplicationLanguages.Languagesプロパティ:ApplicationLanguagesクラス(Windows.Globalization名前空間)のLanguagesプロパティ。「ユーザーが優先する現在のランタイム言語値の順位リスト」
  • GlobalizationPreferences.Languagesプロパティ:GlobalizationPreferencesクラス(Windows.System.UserProfile名前空間)のLanguagesプロパティ。「ユーザーが優先する一連の言語を優先順に取得」

 これら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」に変更し、日本語と英語のリソースを作成する。

アプリの既定の言語を「en-US」に変更し、日本語と英語のリソースを作成した(VS 2013) アプリの既定の言語を「en-US」に変更し、日本語と英語のリソースを作成した(VS 2013)
アプリの既定の言語は、WindowsとPhoneのプロジェクトごとに、「Package.appxmanifest」ファイルを開いて設定する(赤丸内)。
リソースファイル「Resources.resw」は、共有プロジェクトに「Strings\en-US」と「Strings\ja」という2つのパスを切ってから、それぞれに追加する(赤枠内)。

 リソースファイルには最低1つの文字列リソースを言語ごとに違えて定義し、「MainPage.xaml」ファイルなどに表示されるようにしておく(次の画像)。これで、実行時にどの言語の文字列リソースを使用しているかが分かるようになる。

リソースファイルに文字列リソースを定義し、画面に表示されるようにした(VS 2013) リソースファイルに文字列リソースを定義し、画面に表示されるようにした(VS 2013)
英語と日本語のリソースに異なる文字列を定義し、それを画面の右上(通常のタイトルの後ろ)に表示するようにした(赤丸内)。
文字列リソースの定義方法と、それを画面に表示する方法について、詳しくは「WinRT/Metro TIPS:文字列を多言語化対応するには?[Win 8]」および「WinRT/Metro TIPS:文字列リソースを使うには?[Win 8]」をご覧いただきたい。

 次に、共有プロジェクトにユーザーコントロールを追加し、取得した言語を表示するためのUIを作る(次の画像)。

取得した言語を表示するためのユーザーコントロール(VS 2013) 取得した言語を表示するためのユーザーコントロール(VS 2013)
後ほどコードビハインドから文字列をセットするコントロールの名前と、表示する内容は以下の通り。

 ・ CultureInfoText:CultureInfo.CurrentUICultureプロパティの値を表示する。
 ・ ApplicationLanguagesTextBlock:ApplicationLanguages.Languagesプロパティの値を表示する。
 ・ GlobalizationPreferencesTextBlock:GlobalizationPreferences.Languagesプロパティの値を表示する。

また、画面の下の方に見える白い部分(赤枠内)はテキスト表示のサンプルのためのTextBlockコントロールで、名前は「SampleText」とした。冒頭の<UserControl>タグには[Loaded]イベントも定義しておく(赤丸内)。
なお、画面下部に見える[Refresh]ボタンは、OSの設定を変更したときにいちいちアプリを再起動せずに新しい設定を取得させるためのものだ。実装しなくて構わない(次に掲載するコードには含めていない)。

 このユーザーコントロールのコードビハインドに、言語を取得して表示するコードを記述する(次のコード)。

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

言語を取得して表示するコード(上:C#、下:VB)

 このユーザーコントロールをWindowsとPhoneのそれぞれの「MainPage.xaml」画面に貼り付ければ、検証用のアプリは完成だ。

エンドユーザーの言語設定を取得するには?

 上で作ったアプリを使って、3つのAPIの違いを検証しよう。

 検証に当たって、OSの言語設定を次の画像のように変更する(検証が終わったら元に戻してほしい)。優先順位の高い方から、中国語/日本語/英語/ドイツ語とする。本稿の目的は、この一覧の内容を取得する(少なくとも最優先になっている中国語を取得する)ことである。

検証のためのOSの言語設定(Windows)
検証のためのOSの言語設定(Phone) 検証のためのOSの言語設定(上:Windows、下:Phone)
上:Windowsでは、コントロールパネルの右上の検索ボックスで「言語の追加」を検索してこの画面を出す。[言語の追加]リンク(赤丸内)を使って中国語/英語/ドイツ語を追加する。優先順位を変えるには、この画面で言語を選んでおいてから[上へ]/[下へ]のリンク(赤枠内)を使う。
下:Phoneでは、設定画面の[言語]でこの画面を出す。設定を変更するとUIも中国語に変わってしまうので、戻す自信がなければこの検証は行わないでほしい(この画像は設定変更後のもので、UIが中国語に変わっている)。この画面の[+]ボタン(赤枠内)をタップして出てくる言語リストで、必要な言語にチェックを入れ、下端のチェックマークボタンをタップする。優先順位を上げるには、この画面で言語を長押ししてメニューを出して[上に移動](に相当するメニュー項目)を選択する。再起動後に設定変更が有効になる。

 それではアプリを実行してみよう。次の画像のような結果になる。

検証結果(Windows)
検証結果(Phone) 検証結果(上:Windows、下:Phone)
エンドユーザーの設定言語が中国語(言語名の先頭が「zh」で始まる)だと分かるのは、GlobalizationPreferences.Languagesプロパティだけであった。下端のサンプルテキストのLanguageプロパティにはその先頭の言語(zh-Hans-CN)が設定されているため、フォントが中国語のものになっている。

 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

言語設定を変えた検証結果(Windows)
言語設定を変えた検証結果(Phone) 言語設定を変えた検証結果(上:Windows、下:Phone)
言語設定をドイツ語/英語/中国語/日本語の順に変えてから実行した。
日本語よりも英語の優先順位が高いため、アプリが使う文字列リソースは英語になる。
言語設定の先頭にあるドイツ語が適用された下端のサンプルテキストは、Windowsでは日本語のフォント、Phoneでは中国語のフォントで表示される。プラットフォーム側のフォントを選択するロジックが、WindowsとPhoneで異なるためである。

*9 これが日本で俗にいうPhoneの「中華フォント問題」である。しかしこれは、中国におけるWindowsの「日本語フォント問題」でもあるのだ。Languageプロパティの既定値は「en-US」であるため、Languageプロパティに何も指定しなければWindowsでは日本語のフォント、Phoneでは中国語のフォントで表示されるのである。対処するには、適切な言語をLanguageプロパティに設定するか、あるいは適切なフォントを直接指定することになる(適切な言語やフォントの決定方法はケースバイケースだ)。ちなみに、今回のPhoneアプリのページタイトル(=「言語設定を取得する」)には、「Yu Gothic」フォントを直接指定してある。


まとめ

 エンドユーザーがOSに設定している言語を取得するには、GlobalizationPreferences.Languagesプロパティを使う。OSの設定言語とアプリの対応言語の組み合わせからシステムが決定した実行中のアプリの言語は、CultureInfo.CurrentUICultureプロパティかApplicationLanguages.Languagesプロパティで取得できる。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。