複数言語のリソースを用意しておいて、システムの言語を変えると、アプリで使用される言語リソースも変わるわけですが、システム設定に関係なく、アプリ内で言語選択をしたい。

つまり、

これ。
Kotlin でもできたので、どうせならということで Xamarin.Android でもやってみました。

できあがり

こんな感じの成果になります。

方法

1. 多言語用のリソースファイルを用意する

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>

2. MainActivity を実装する

とりあえずざっと。

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_JPen_US を SharedPreference に保存しておき、Activity を再起動します。

再起動直後に AttachBaseContext() が呼ばれるので、そこで SharedPreference に記憶された Locale を読み出しています。

おまけ

1st try では、SharedPref を使うのを面倒くさがって、Application クラスに記憶させとく作戦でしたが、失敗しました。その原因は AttachBaseContext()OnCreate() よりも先に呼ばれ、さらに AttachBaseContext() の時点では Activity.Applicationnull になっているためでした。

amay077

Microsoft MVP(Xamarin). フルリモートワーカー. Geospatial Mobile app developer. Love C#.

amay077 amay077