アプリ内でロケールを変更する


197

ユーザーはアプリ内のロケールを変更できます(電話の設定は英語のままにして、アプリのコンテンツをフランス語、オランダ語、またはその他の言語で読むこともできます...)

1.5 / 1.6ではこれが完全に機能するのに、2.0では機能しないのはなぜですか?

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
    case 201:
        Locale locale2 = new Locale("fr"); 
        Locale.setDefault(locale2);
        Configuration config2 = new Configuration();
        config2.locale = locale2;
        getBaseContext().getResources().updateConfiguration(
            config2, getBaseContext().getResources().getDisplayMetrics());
        // loading data ...
        refresh();
        // refresh the tabs and their content
        refresh_Tab ();   
     break;
     case 201: etc...

問題は、ユーザーが上記のコード行を実行するたびに、メニューがますます「縮小」することです...

これは縮小されるメニューです:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(0, 100, 1, "REFRESH").setIcon(android.R.drawable.ic_menu_compass);
    SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
        langMenu.add(1, 201, 0, "Nederlands");
        langMenu.add(1, 202, 0, "Français");
    menu.add(0, 250, 4, R.string.OptionMenu2).setIcon(android.R.drawable.ic_menu_send);
    menu.add(0, 300, 5, R.string.OptionMenu3).setIcon(android.R.drawable.ic_menu_preferences);
    menu.add(0, 350, 3, R.string.OptionMenu4).setIcon(android.R.drawable.ic_menu_more);
    menu.add(0, 400, 6, "Exit").setIcon(android.R.drawable.ic_menu_delete);

    return super.onCreateOptionsMenu(menu);
}

APIレベル5でこれを再び機能させるにはどうすればよいですか?

これをテストする場合の完全なコードは次のとおりです。

import java.util.Locale;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;

public class Main extends Activity {
    /** Called when the activity is first created. */


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
            langMenu.add(1, 201, 0, "Nederlands");
            langMenu.add(1, 202, 0, "Français");

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){

        case 201:

            Locale locale = new Locale("nl"); 
            Locale.setDefault(locale);
            Configuration config = new Configuration();
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
            Toast.makeText(this, "Locale in Nederlands !", Toast.LENGTH_LONG).show();
            break;

        case 202:

            Locale locale2 = new Locale("fr"); 
            Locale.setDefault(locale2);
            Configuration config2 = new Configuration();
            config2.locale = locale2;
            getBaseContext().getResources().updateConfiguration(config2, getBaseContext().getResources().getDisplayMetrics());

            Toast.makeText(this, "Locale en Français !", Toast.LENGTH_LONG).show();
            break;  

        }
        return super.onOptionsItemSelected(item);
    }
}

そしてここがマニフェストです:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cousinHub.ChangeLocale"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Main"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" /> 
    </manifest>

これが私が見つけたものです:

<uses-sdk android:minSdkVersion="5" />

=>それはちょうどうまくいく...

<uses-sdk android:minSdkVersion="3" />

=>ロケールを変更するたびにメニューが縮小します!!!

1.5のユーザーがアプリケーションにアクセスできるようにしたいので、どうすればよいですか?


「縮小」とはどういう意味ですか?
CommonsWare 2010

毎回小さくなります。getBaseContext以外のものを使用する必要がありますか?
Hubert

ロケールの変更だけを行う非常に基本的なアプリを実行するとき、私は同じ「メニューの縮小」を持っていないので、多分それは問題を作成するこれらの行ではありません。私のアクティビティは実際にはTabActivityだったからだと思いましたが、それでも問題を再現することはできません。このバグの正確な原因をさらに調査する必要があります。それ以上検索しないでください。見つけたらこちらに回答を掲載します。乾杯、H.
ヒューバート

私は最初の投稿を編集して、問題を作成する方法の例を示しました。そして、実際に<uses-sdk android:minSdkVersion = "5" />という行を "3"に変更すると、実際に問題が発生することに気づきました。
Hubert

1
言語リスト、設定画面の設定を提供し、アプリケーションの言語をオーバーライドする次のライブラリを使用できます:github.com/delight-im/Android-Languages
caw

回答:


151

元の質問では、ロケール自体に関するものではなく、他のすべてのロケール関連の質問がこの質問を参照しています。そのため、ここで問題を明確にしたいと思いました。私はこの質問を自分のロケール切り替えコードの出発点として使用しましたが、この方法は正確ではないことがわかりました。これは機能しますが、構成が変更されるまで(画面の回転など)、その特定のアクティビティでのみ機能します。しばらくコードをいじってみたところ、次のようなアプローチになりました。

私はandroid.app.Applicationを拡張し、次のコードを追加しました:

public class MyApplication extends Application
{
    private Locale locale = null;

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        if (locale != null)
        {
            newConfig.locale = locale;
            Locale.setDefault(locale);
            getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
        }
    }

    @Override
    public void onCreate()
    {
        super.onCreate();

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        Configuration config = getBaseContext().getResources().getConfiguration();

        String lang = settings.getString(getString(R.string.pref_locale), "");
        if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang))
        {
            locale = new Locale(lang);
            Locale.setDefault(locale);
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
        }
    }
}

このコードにより、すべてのアクティビティにカスタムロケールが設定され、ローテーションやその他のイベントでリセットされなくなります。

また、設定変更をすぐに適用するために多くの時間を費やしましたが、成功しませんでした。アクティビティの再起動時に言語が正しく変更されましたが、数値形式とその他のロケールプロパティは、アプリケーションが完全に再起動するまで適用されませんでした。

への変更 AndroidManifest.xml

追加することを忘れないでくださいandroid:configChanges="layoutDirection|locale"AndroidManifestのすべての活動に、同様android:name=".MyApplication"<application>要素。


1
あなたの好みの活動はあなたの主な活動から呼び出されていますか?これを再開時に配置できます... @Override protected void onResume(){if(!(PreferenceManager.getDefaultSharedPreferences(getApplicationContext())。getString( "listLanguage"、 "en").equals(langPreference))){ refresh(); super.onResume(); } private void refresh(){finish(); Intent myIntent = new Intent(Main.this、Main.class); startActivity(myIntent); }
trgraglia

1
これは実際に私が必要とするもので、MainActivityでupdateConfiguration()を呼び出すために使用しましたが、アプリケーションが終了してから起動してもメニューが更新されません。あなたの解決策は正しいです。文字列をすぐに更新したい場合でも、手動で文字列をリセットする必要があると思います。(例:ボタンラベルのリセットまたはメニューの再作成)。
emeraldhieu 2010年

これはAndroidシステムのロケールに干渉しますか?
Kostadin

17
(注)は、必要があることを決して Androidはあなたを与えることConfigurationオブジェクトを変更しない(confignewConfig)。config = new Configuration(config);最初にコピーを作成する必要があります。問題を見つける前に、このコードのデバッグに1時間費やしました(アクティビティが際限なく点滅していました)。
imgx64 14年

1
Android 7以降では、コールドスタートからアプリを起動すると、システムフォントサイズが大きく設定されているため、この手法でグリッチが発生します。GoogleはupdateConfiguration()動作方法を変更したため、アクティビティが2つの異なる言語(特にダイアログ)で表示される場合があります。さらに詳しい情報:stackoverflow.com/questions/39705739/...
MR-IDE

61

ぐっすり眠った後、私はWebで答えを見つけました(次の行 " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"で簡単なGoogle検索)、ここにあります:

リンクテキスト =>このリンクはscreenshots、何が起こっているかを示しています!

ここ密度が問題でした、私はこれをAndroidManifest.xmlに含める必要がありました

<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true"
/>

最も重要なのはandroid:anyDensity = "true"です。

AndroidManifest.xmlすべてのアクティビティ(Android 4.1以下の場合)に以下を追加することを忘れないでください。

android:configChanges="locale"

このバージョンは、Android 4.2(APIレベル17)向けにビルドするときに必要です

android:configChanges="locale|layoutDirection"

3
Googleのアドバイスにより、android:anyDensity = "false"がありました(レガシーアプリケーションの戦略-ポイント9:developer.android.com/intl/fr/guide/practices/…)。気をつけて!
Hubert

2
ロケールを変更してもボタンのテキストが変わりません。文字列リソースを含むすべてのウィジェットを更新するための解決策はありますか?OnRestart()でsetText(getString(R.string.button_label))を使用してテキストを再描画しています。(もちろん、アプリケーションの再起動後に変更されます。)
emeraldhieu

recreateアクティビティを呼び出すことができますactivity.recreate()
Kra

59

Android Mでは、トップのソリューションは機能しません。アプリケーションクラスとすべてのアクティビティから呼び出す必要があるものを修正するヘルパークラスを作成しました(BaseActivityを作成してから、すべてのアクティビティにそれを継承させることをお勧めします。

:これにより、RTLレイアウトの方向も正しくサポートされます。

ヘルパークラス:

public class LocaleUtils {

    private static Locale sLocale;

    public static void setLocale(Locale locale) {
        sLocale = locale;
        if(sLocale != null) {
            Locale.setDefault(sLocale);
        }
    }

    public static void updateConfig(ContextThemeWrapper wrapper) {
        if(sLocale != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Configuration configuration = new Configuration();
            configuration.setLocale(sLocale);
            wrapper.applyOverrideConfiguration(configuration);
        }
    }

    public static void updateConfig(Application app, Configuration configuration) {
        if (sLocale != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            //Wrapping the configuration to avoid Activity endless loop
            Configuration config = new Configuration(configuration);
            // We must use the now-deprecated config.locale and res.updateConfiguration here,
            // because the replacements aren't available till API level 24 and 17 respectively.
            config.locale = sLocale;
            Resources res = app.getBaseContext().getResources();
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
    }
}

応用:

public class App extends Application {
    public void onCreate(){
        super.onCreate();

        LocaleUtils.setLocale(new Locale("iw"));
        LocaleUtils.updateConfig(this, getBaseContext().getResources().getConfiguration());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleUtils.updateConfig(this, newConfig);
    }
}

BaseActivity:

public class BaseActivity extends Activity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}

11
これは受け入れられるべき答えです。UPDATECONFIGがコンストラクタからではないアクティビティクラスのonCreateのから呼び出さなければならないことを確認し、私は...この間違いをしていた
Hitesh Sahu

1
これをプログラムにどのように実装したかによって異なります。onConfigurationChanged()をオーバーライドした場合、.updateConfig()を呼び出す必要はありません。2度呼び出されているため、エラーが発生します。.setLocale()の呼び出しのみが機能していないと言っているのは、アクティビティでrecreate()を呼び出すか、アクティビティ/フラグメントで独自のメソッドを呼び出す必要があるためです(私は前者を優先しますスムーズではありませんが、問題を心配する必要はありません)
RJFares

3
// android.support.v7.view.ContextThemeWrapper;をインポートします import android.view.ContextThemeWrapper; // correct namespace
Mehdi Rostami '27

1
BaseActivity()コンストラクタは呼び出すべきではありませんsuper()か?
LarsH 16

1
このソリューションに<application android:name=".App">は、マニフェストを含めることが暗黙的に含まれていますか?android:configChanges="layoutDirection|locale"マニフェストのすべてのアクティビティに追加してみませんか?
LarsH 16

10

これはAndreyの回答に対する私のコメント用ですが、コメントにコードを含めることはできません。

あなたの好みの活動はあなたの主な活動から呼び出されていますか?これを再開時に配置できます...

@Override
protected void onResume() {
    if (!(PreferenceManager.getDefaultSharedPreferences(
            getApplicationContext()).getString("listLanguage", "en")
            .equals(langPreference))) {
        refresh();
    }
    super.onResume();
}

private void refresh() {
    finish();
    Intent myIntent = new Intent(Main.this, Main.class);
    startActivity(myIntent);
}

2
実行時に言語を変更したい場合は、そのアクティビティを終了し、変更を取得するためにそのアクティビティを再度開始する必要がありますか?アクティビティを終了して再び開始するたびに良いとは限らないため、変更する他の方法はありますか?
Shreyash Mahajan 2013

1
より良い方法activity.recreate()
Kra

@trgradlia、私は上記のコードを使用したいと思います。langPreferenceとは何ですか?最近変更された言語と以前の言語を比較していると思います。
Mahen、2017年

1
アクティビティにはrecreate()メソッドがあるため、コードのrecreate()代わりに使用refresh()します。これにより、3行のコードがrefresh()メソッドに保存されます
Inzimam Tariq IT

1
@InzimamTariqIT、ヒントをありがとう...答えはほぼ7年前なので、そのままにしておきます。コメントであなたの改善点を見つけてください。
trgraglia

6

ゲーム内のオブジェクトが完全に異なる位置に配置されるため、android:anyDensity = "true"を使用できませんでした...これもトリックを行うようです:

//ロケールを作成します
ロケールlocale2 = new Locale(loc); 
Locale.setDefault(locale2);
構成config2 = new Configuration();
config2.locale = locale2;

//ロケールを更新します
mContext.getResources()。updateConfiguration(config2、null);

このソリューションは、これを使用したばかりの私に最適ですcode Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk

1

ロケールをすぐに変更するためのメニューオプションに影響を与えたい場合は、次のようにする必要があります。

//onCreate method calls only once when menu is called first time.
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    //1.Here you can add your  locale settings . 
    //2.Your menu declaration.
}
//This method is called when your menu is opend to again....
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    menu.clear();
    onCreateOptionsMenu(menu);
    return super.onMenuOpened(featureId, menu);
}

しかし、このソリューションは、開いているすべてのメニューのメニューをクリアしませんか?onMenuOpenedロケールの変更後、1回だけ呼び出す必要があると思います。
トランテ2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.