更改应用程序本身的语言环境

时间:2023-01-28 22:39:31

My users can change the Locale within the app (they may want to keep their phone settings in English but read the content of my app in French, Dutch or any other language ...)

我的用户可以在应用程序中更改语言环境(他们可能想用英语保持他们的手机设置,但是用法语、荷兰语或其他语言阅读我的应用程序的内容……)

Why is this working perfectly fine in 1.5/1.6 but NOT in 2.0 anymore ???

为什么这个在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...

The problem is that the MENU "shrinks" more and more everytime the user is going through the lines of code above ...

问题是,每当用户通过上面的代码行时,菜单“缩小”得越来越多……

This is the Menu that gets shrunk:

这是缩小的菜单:

@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);
}

What should I do in API Level 5 to make this work again ?

在API级别5中,我应该做些什么才能让它再次工作呢?

HERE IS THE FULL CODE IF YOU WANT TO TEST THIS :

如果你想测试的话,这里有完整的代码:

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);
    }
}

AND HERE IS THE MANIFEST :

这是清单:

<?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>

THIS IS WHAT I FOUND :

我发现:

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

=> IT WORKS JUST FINE ...

=>运行良好……

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

=> Menu shrinks every time you change the locale !!!

=>菜单每次你改变语言环境都会缩小!!

as I want to keep my application accessible for users on 1.5, what should I do ??

由于我想让我的应用程序对1.5的用户具有可访问性,我应该怎么做?

6 个解决方案

#1


144  

Through the original question is not exactly about the locale itself all other locale related questions are referencing to this one. That's why I wanted to clarify the issue here. I used this question as a starting point for my own locale switching code and found out that the method is not exactly correct. It works, but only until any configuration change (e.g. screen rotation) and only in that particular Activity. Playing with a code for a while I have ended up with the following approach:

通过最初的问题并不完全是关于语言环境本身的,所有与语言环境相关的问题都与此相关。这就是为什么我想在这里澄清这个问题。我用这个问题作为我自己的语言环境切换代码的起点,发现这个方法并不完全正确。它只在任何配置改变(例如屏幕旋转)之前有效,并且只在特定的活动中有效。玩了一段时间的代码后,我得到了以下方法:

I have extended android.app.Application and added the following code:

我有延长android.app。申请及增加以下代码:

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());
        }
    }
}

This code ensures that every Activity will have custom locale set and it will not be reset on rotation and other events.

这段代码确保每个活动都有自定义的语言环境设置,并且不会在旋转和其他事件时重置。

I have also spent a lot of time trying to make the preference change to be applied immediately but didn't succeed: the language changed correctly on Activity restart, but number formats and other locale properties were not applied until full application restart.

我还花了很多时间试图立即应用首选项更改,但没有成功:在活动重新启动时,语言正确地更改了,但是直到完全重新启动应用程序时才应用数字格式和其他语言环境属性。

Changes to AndroidManifest.xml

Don't forget to add android:configChanges="layoutDirection|locale" to every activity at AndroidManifest, as well as the android:name=".MyApplication" to the <application> element.

不要忘记添加android:configChanges=“layoutDirection|locale”到AndroidManifest的每个活动,以及android:name=”。“MyApplication”到 元素。

#2


60  

After a good night of sleep, I found the answer on the Web (a simple Google search on the following line "getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), here it is :

睡了一个好觉之后,我在Web上找到了答案(在下面一行“getBaseContext(). getresources()”中进行简单的谷歌搜索)。updateConfiguration(mConfig、getBaseContext().getResources().getDisplayMetrics());

link text => this link also shows screenshots of what is happening !

这个链接也显示了正在发生的事情的屏幕截图!

Density was the issue here, I needed to have this in the AndroidManifest.xml

这里的问题是密度,我需要在AndroidManifest.xml中包含这个

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

The most important is the android:anyDensity =" true ".

最重要的是android:anyDensity =“true”。

Don't forget to add the following in the AndroidManifest.xml for every activity (for Android 4.1 and below):

不要忘记在AndroidManifest中添加以下内容。每个活动的xml (Android 4.1及以下):

android:configChanges="locale"

This version is needed when you build for Android 4.2 (API level 17) explanation here:

当您为Android 4.2 (API级别17)构建时需要这个版本:

android:configChanges="locale|layoutDirection"

#3


48  

In Android M the top solution won't work. I've written a helper class to fix that which you should call from your Application class and all Activities (I would suggest creating a BaseActivity and then make all the Activities inherit from it.

在Android M中,*解决方案行不通。我已经编写了一个帮助类来修复您应该从应用程序类和所有活动中调用的类(我建议创建一个BaseActivity,然后让所有的活动都继承它)。

Note: This will also support properly RTL layout direction.

注意:这也支持正确的RTL布局方向。

Helper class:

助手类:

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());
        }
    }
}

Application:

应用程序:

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:

BaseActivity:

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

#4


8  

This is for my comment on Andrey's answer but I cant include code in the comments.

这是我对Andrey的回答的评论,但是我不能在评论中包含代码。

Is you preference activity being called from you main activity? you could place this in the on resume...

您的首选项活动是从您的主活动调用的吗?你可以把这个放在简历上…

@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);
}

#5


5  

I couldn't used android:anyDensity="true" because objects in my game would be positioned completely different... seems this also does the trick:

我不能使用android:anyDensity=“true”,因为我的游戏中的对象位置完全不同……看来这也是个诀窍:

// creating locale
Locale locale2 = new Locale(loc); 
Locale.setDefault(locale2);
Configuration config2 = new Configuration();
config2.locale = locale2;

// updating locale
mContext.getResources().updateConfiguration(config2, null);

#6


0  

If you want to effect on the menu options for changing the locale immediately.You have to do like this.

如果您想要对菜单选项产生影响,请立即更改语言环境。你必须这样做。

//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);
}

#1


144  

Through the original question is not exactly about the locale itself all other locale related questions are referencing to this one. That's why I wanted to clarify the issue here. I used this question as a starting point for my own locale switching code and found out that the method is not exactly correct. It works, but only until any configuration change (e.g. screen rotation) and only in that particular Activity. Playing with a code for a while I have ended up with the following approach:

通过最初的问题并不完全是关于语言环境本身的,所有与语言环境相关的问题都与此相关。这就是为什么我想在这里澄清这个问题。我用这个问题作为我自己的语言环境切换代码的起点,发现这个方法并不完全正确。它只在任何配置改变(例如屏幕旋转)之前有效,并且只在特定的活动中有效。玩了一段时间的代码后,我得到了以下方法:

I have extended android.app.Application and added the following code:

我有延长android.app。申请及增加以下代码:

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());
        }
    }
}

This code ensures that every Activity will have custom locale set and it will not be reset on rotation and other events.

这段代码确保每个活动都有自定义的语言环境设置,并且不会在旋转和其他事件时重置。

I have also spent a lot of time trying to make the preference change to be applied immediately but didn't succeed: the language changed correctly on Activity restart, but number formats and other locale properties were not applied until full application restart.

我还花了很多时间试图立即应用首选项更改,但没有成功:在活动重新启动时,语言正确地更改了,但是直到完全重新启动应用程序时才应用数字格式和其他语言环境属性。

Changes to AndroidManifest.xml

Don't forget to add android:configChanges="layoutDirection|locale" to every activity at AndroidManifest, as well as the android:name=".MyApplication" to the <application> element.

不要忘记添加android:configChanges=“layoutDirection|locale”到AndroidManifest的每个活动,以及android:name=”。“MyApplication”到 元素。

#2


60  

After a good night of sleep, I found the answer on the Web (a simple Google search on the following line "getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), here it is :

睡了一个好觉之后,我在Web上找到了答案(在下面一行“getBaseContext(). getresources()”中进行简单的谷歌搜索)。updateConfiguration(mConfig、getBaseContext().getResources().getDisplayMetrics());

link text => this link also shows screenshots of what is happening !

这个链接也显示了正在发生的事情的屏幕截图!

Density was the issue here, I needed to have this in the AndroidManifest.xml

这里的问题是密度,我需要在AndroidManifest.xml中包含这个

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

The most important is the android:anyDensity =" true ".

最重要的是android:anyDensity =“true”。

Don't forget to add the following in the AndroidManifest.xml for every activity (for Android 4.1 and below):

不要忘记在AndroidManifest中添加以下内容。每个活动的xml (Android 4.1及以下):

android:configChanges="locale"

This version is needed when you build for Android 4.2 (API level 17) explanation here:

当您为Android 4.2 (API级别17)构建时需要这个版本:

android:configChanges="locale|layoutDirection"

#3


48  

In Android M the top solution won't work. I've written a helper class to fix that which you should call from your Application class and all Activities (I would suggest creating a BaseActivity and then make all the Activities inherit from it.

在Android M中,*解决方案行不通。我已经编写了一个帮助类来修复您应该从应用程序类和所有活动中调用的类(我建议创建一个BaseActivity,然后让所有的活动都继承它)。

Note: This will also support properly RTL layout direction.

注意:这也支持正确的RTL布局方向。

Helper class:

助手类:

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());
        }
    }
}

Application:

应用程序:

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:

BaseActivity:

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

#4


8  

This is for my comment on Andrey's answer but I cant include code in the comments.

这是我对Andrey的回答的评论,但是我不能在评论中包含代码。

Is you preference activity being called from you main activity? you could place this in the on resume...

您的首选项活动是从您的主活动调用的吗?你可以把这个放在简历上…

@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);
}

#5


5  

I couldn't used android:anyDensity="true" because objects in my game would be positioned completely different... seems this also does the trick:

我不能使用android:anyDensity=“true”,因为我的游戏中的对象位置完全不同……看来这也是个诀窍:

// creating locale
Locale locale2 = new Locale(loc); 
Locale.setDefault(locale2);
Configuration config2 = new Configuration();
config2.locale = locale2;

// updating locale
mContext.getResources().updateConfiguration(config2, null);

#6


0  

If you want to effect on the menu options for changing the locale immediately.You have to do like this.

如果您想要对菜单选项产生影响,请立即更改语言环境。你必须这样做。

//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);
}