複数言語のリソースを用意しておいて、システムの言語を変えると、アプリで使用される言語リソースも変わるわけですが、システム設定に関係なく、アプリ内で言語選択をしたい。
つまり、
これ。 Kotlin でもできたので、どうせならということで Xamarin.Android でもやってみました。
こんな感じの成果になります。
Android の仕様に従って values/
や values-ja-rJP/
に String.xml
を用意します。
ファイルを追加した後で、Build Action が「AndroidResource」になっている事を確認してください。
values/String.xml (英語ってかデフォルト):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ResourceTest</string>
<string name="welcome">WELCOME</string>
<string name="to_japanese">To Japanese</string>
<string name="to_english">To English</string>
</resources>
values-ja-rJP/String.xml (日本語):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ResourceTest</string>
<string name="welcome">ようこそ</string>
<string name="to_japanese">日本語にする</string>
<string name="to_english">英語にする</string>
</resources>
とりあえずざっと。
MainActivity.cs:
using Android.App;
using Android.Widget;
using Android.OS;
using Android.Content;
using Android.Content.Res;
using Java.Util;
using System.Linq;
using System;
namespace ResourceTest
{
[Activity(Label = "ResourceTest", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity
{
protected override void AttachBaseContext(Context baseContext)
{
var pref = baseContext.GetSharedPreferences("mypref", FileCreationMode.Private);
var locale = pref.GetString("locale", string.Empty);
var newLocale = Locale.GetAvailableLocales().FirstOrDefault(
l => string.Equals(l.ToString(), locale, StringComparison.OrdinalIgnoreCase))
?? Locale.Default;
var configuration = baseContext.Resources.Configuration;
configuration.Locale = newLocale;
var newContext = new ContextWrapper(
baseContext.CreateConfigurationContext(configuration));
base.AttachBaseContext(newContext);
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
FindViewById<TextView>(Resource.Id.textWelcome).Text = Resources.GetString(Resource.String.welcome);
var pref = this.GetSharedPreferences("mypref", FileCreationMode.Private);
FindViewById<Button>(Resource.Id.buttonToEnglish).Click += (sender, e) =>
{
var editor = pref.Edit();
editor.PutString("locale", "en_US");
editor.Commit();
Restart();
};
FindViewById<Button>(Resource.Id.buttonToJapanese).Click += (sender, e) =>
{
var editor = pref.Edit();
editor.PutString("locale", "ja_JP");
editor.Commit();
Restart();
};
}
private void Restart()
{
var intent = new Intent(this, typeof(MainActivity));
this.Finish();
this.StartActivity(intent);
}
}
}
簡単に説明すると 「AttachBaseContext()
を override して、そこで任意の Locale に変えた Context
にすげ替え」ています。
「任意の Locale」は、2つのボタンを押したときにそれぞれ ja_JP
、en_US
を SharedPreference に保存しておき、Activity を再起動します。
再起動直後に AttachBaseContext()
が呼ばれるので、そこで SharedPreference に記憶された Locale を読み出しています。
1st try では、SharedPref を使うのを面倒くさがって、Application
クラスに記憶させとく作戦でしたが、失敗しました。その原因は AttachBaseContext()
は OnCreate()
よりも先に呼ばれ、さらに AttachBaseContext()
の時点では Activity.Application
が null
になっているためでした。