使用resConfig为Android flavor强制设定语言环境

19

我正在尝试使用Android Build系统中的resConfig和resConfigs。

  • Android Studio版本为1.2.2
  • Gradle构建版本为1.2.3
  • OSX 10.10.3

我的项目在使用这两个选项时遇到了问题,所以我在Android Studio中新建了一个空白项目。我在build.gradle文件中添加了resConfigs "en"、"fr"。

android { 
   defaultConfig {
        ...
        resConfigs "en", "fr" 
        ...
   }
}

并定义了两种基本口味

productFlavors {
        fr {
            resConfig "fr"
        }

        en {
            resConfig "en"
        }
}

我随后创建了2个strings.xml文件并翻译了hello_world默认标签。

/src/main/res/values/strings.xml(默认)
/src/main/res/values-en/strings.xml
/src/main/res/values-fr/strings.xml

这样,我希望在MyApplication/app/builds/intermediates/res/en/debug中只看到3个值文件夹,因为我已经在resConfigs中定义了仅使用“en”和“fr”并过滤其他任何内容。

/values/
/values-en/
/values-fr/

虽然所有语言的values文件夹仍然在那里,所以resConfigs显然没有过滤任何内容。

当我运行enDebug flavor变体时,我还期望从values-en中看到hello_world标签,因为我指定了flavor en将使用resConfig“en”,但是当我运行测试应用程序时,我看到的标签来自于/values-fr/strings.xml而不是/values-en/strings.xml,因为平板电脑的语言设置为法语加拿大。

我是否误解了resConfig的目的?如果是这样,我该如何强制一个flavor仅在一种语言中运行并且不依赖于操作系统的区域设置?此解决方案必须与flavorDimensions兼容,因为我在尝试配置的实际应用程序中使用它们。

注意:因为我认为这个resConfig行为确实存在问题,所以我也提交了一个bug报告https://code.google.com/p/android/issues/detail?id=180694

谢谢

2个回答

15
经过多个小时的努力,我终于找到了解决方案,以下是针对同样问题的人们的解决方法。
原来,resConfig不是我所认为的那样。我找到的唯一正确的方式是强制特定版本的locale,方法如下:
首先,确保所有的活动都扩展了一个常见的活动,该活动将负责强制设置locale。(我不得不创建2个活动,因为有些活动扩展了FragmentActivity,有些扩展了Activity。我也不太喜欢在活动构造函数中这样做,但由于我必须在任何调用getResources之前调用applyOverrideConfiguration,所以我需要在活动的onStart之前调用它)。
public class LocaleMainActivity extends Activity {

    public LocaleMainActivity() {
        ActivityUtils.forceLocale(this);
    }
}

public class LocaleMainFragmentActivity extends FragmentActivity {

    public LocaleMainFragmentActivity() {
        ActivityUtils.forceLocale(this);
    }
}

//Method from ActivityUtils
public static void forceLocale(ContextThemeWrapper contextThemeWrapper) {
    Configuration configuration = new Configuration();
    Locale defaultLocale = new Locale(BuildConfig.LOCALE);
    configuration.locale = defaultLocale;
    contextThemeWrapper.applyOverrideConfiguration(configuration);      
}

接下来,您需要在build.gradle的flavors中定义语言环境。
productFlavors {

    myFrenchFlavor {
     buildConfigField "String", "LOCALE", "\"fr\""
    }
    myEnglishFlavor {
      buildConfigField "String", "LOCALE", "\"en\""
    }
}

I tested the solution on API 19+. Works when you change language while in the app too.
备选方案(不适用于Android M+,请参考安卓bug报告) 这个方案在stackOverflow上很流行,但我在Android M上实现时遇到了困难,请记住这一点。
在AndroidApplication的onCreate()中,您可以调用updateConfiguration并设置地区。
Locale myLocale = new Locale(BuildConfig.LOCALE);
Resources res = getResources();
Configuration conf = res.getConfiguration();
conf.locale = myLocale;
res.updateConfiguration(conf, res.getDisplayMetrics());

如果您在onConfigurationChanged()中定义了内容,您还需要重置它。

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);            
        Configuration config = new Configuration(newConfig);
        config.locale = new Locale(BuildConfig.LOCALE);
        getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
    }
编辑:请注意,applyOverrideConfiguration目前有一个副作用,如此处所述Chromium webview does not seems to work with Android applyOverrideConfiguration。Chromium团队正在解决这个问题! 谢谢。

2
顺便提一下(因为我是您上面链接的错误报告的提交者),上述提到的替代解决方案在使用来自“Activity”而不是“Application”的“context”时确实适用于M。 - ahmedre
尝试了这个解决方案,适用于API级别大于等于17的设备,可以正常工作。我唯一担心的问题是在Activity的构造函数中设置值,这在Android组件初始化中有点反模式。您是否曾经遇到过使用此方法时出现崩溃或错误的情况? - patrickjason91

6
resConfig 不会覆盖 defaultConfig 中指定的 resConfigs,而只是将它们合并在一起。因此,你最终会得到包含所有风味的 enfr 资源。
为了避免这种情况,你不应该在 defaultConfig 中指定任何 resConfigs,而是在特定的风味中设置它。
此外,应用程序始终会包括默认的 values,这意味着如果你在那里有英文字符串,则在设备设置为非法语区域设置的情况下,fr 风味仍将显示出英文内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接