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


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

问题是,每次用户浏览上面的代码行时,MENU都会越来越“缩小”。

这是缩小的菜单:

@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之外的其他东西吗?
休伯特

也许不是这些行造成了问题,因为当我做一个非常基本的应用仅更改Locale时,我没有相同的“菜单缩小”。我以为这可能是因为我的Activity实际上是一个TabActivity,但是即使那样,我也无法重新创建问题。我将不得不进一步调查此错误的确切原因是什么...请勿再搜索。找到答案后,我会在此处发布答案。干杯,H。–
休伯特

我编辑了第一篇文章,举例说明了如何创建问题。而且我实际上注意到,当我将<uses-sdk android:minSdkVersion =“ 5” />更改为“ 3”时,确实出现了问题!
休伯特

1
您可以使用下面的库,它提供的语言列表中,偏好设置屏幕,并覆盖在你的应用程序的语言:github.com/delight-im/Android-Languages
CAW

Answers:


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

这段代码可确保每个Activity都具有自定义的语言环境设置,并且不会在轮播和其他事件上重置。

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

更改为 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(); 意图myIntent = new意图(Main.this,Main.class); startActivity(myIntent); }
trgraglia 2011年

1
这实际上是我所需要的,我曾经在MainActivity中调用updateConfiguration(),但是当Application终止然后启动时,菜单不会更新。您的解决方案是正确的。如果您想立即刷新字符串,我认为您仍然必须手动重置字符串。(例如,重置按钮标签或重新创建菜单)。
emeraldhieu 2011年

这会干扰Android系统的语言环境吗?
Kostadin

17
请注意,您绝不能修改Android为您提供的Configuration对象(confignewConfig)。您应该config = new Configuration(config);先复制一份。我花了一个小时调试此代码,然后才发现问题(这使活动不断闪烁)。
imgx64 2014年

1
从冷启动启动应用程序时,此技术在Android 7+上会出现故障,系统字体大小设置为大。Google更改了工作方式updateConfiguration(),因此有时您的活动将以2种不同的语言显示(尤其是在对话框中)。更多信息:stackoverflow.com/questions/39705739/...
MR-IDE

61

经过一夜的睡眠,我在网上找到了答案(在下面的“ 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的建议(传统应用程序策略-第9点:developer.android.com/intl/fr/guide/practices/…),我使用了android:anyDensity =“ false” 。然后要小心!
休伯特

2
更改语言环境后,按钮上的文本不会更改。我想知道是否有任何解决方案可以刷新所有包含字符串资源的小部件。现在,我通过在OnRestart()中使用setText(getString(R.string.button_label))重绘文本。(当然,它会在重新启动应用程序后发生变化。)
emeraldhieu 2011年

您可以致电“重新创建活动”activity.recreate()

59

在Android M中,最佳解决方案不起作用。我已经编写了一个帮助程序类,用于修复应从Application类和所有Activity(我建议创建一个BaseActivity,然后使所有Activity继承自该类)中进行调用的问题。

注意:这也将支持正确的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叫,我在做这个错误..
亚太区首席技术官Matt萨胡

1
这取决于您在程序中如何实现。如果您重写了onConfigurationChanged(),则无需调用.updateConfig(),这就是得到错误的原因,因为该错误被调用了两次。如果您说仅调用.setLocale()不起作用,那是因为您需要通过在活动上调用recreate()或在活动/片段中使用您自己的方法来刷新UI(我确实更喜欢前者,即使它不是很平滑,但是您永远不会担心问题)
RJFares

3
//导入android.support.v7.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; //正确的名称空间
Mehdi Rostami,2016年

1
BaseActivity()构造函数不应该调用super()吗?
LarsH 2016年

1
此解决方案是否隐式包括放入<application android:name=".App">清单?如何添加android:configChanges="layoutDirection|locale"到清单中的每个活动?
LarsH 2016年

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

@trgradlia,我想使用上面的代码。请告诉我,什么是langPreference?我认为我们正在比较最近更改的语言和以前的语言。
Mahen's

1
活动有recreate()方法,以便使用recreate()来代替refresh()你的代码。这将在refresh()方法中保存您的3行代码
Inzimam Tariq IT

1
@InzimamTariqIT,谢谢您的提示...答案将近7岁了,所以我将其保留不变,人们可以在评论中找到您的改进。
trgraglia

6

我无法使用android:anyDensity =“ true”,因为游戏中的对象的位置会完全不同...看来这也可以解决问题:

//创建语言环境
Locale locale2 =新的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 2011年

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在更改语言环境后,应该只调用一次。
trante
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.