Force locale for Android flavor with resConfig

Eric Labelle picture Eric Labelle · Jul 22, 2015 · Viewed 15.1k times · Source

I am trying to use the resConfig and resConfigs from the Android Build system.

  • Android Studio version 1.2.2
  • Gradle build version 1.2.3
  • OSX 10.10.3

I was having problem with these 2 options with my project so I started a new blank project with android studio. I attached my build.gradle where I only added resConfigs "en", "fr" under

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

And defined 2 basic flavors

productFlavors {
        fr {
            resConfig "fr"
        }

        en {
            resConfig "en"
        }
}

I then created a 2 strings.xml files and translated the hello_world default label

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

With this, I would expect to see only 3 values folders in MyApplication/app/builds/intermediates/res/en/debug since I defined in the resConfigs to only use "en" and "fr" and filter anything else

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

Although, all languages values folder are still in there so the resConfigs is not filtering anything apparently.

I would also expect to see the label hello_world from values-en when running enDebug flavor variant since I specified that the flavor en would use resConfig "en" although when I run the test app I see the label from /values-fr/strings.xml instead of /values-en/strings.xml since the language on the tablet is configured as French Canada

Am I misunderstanding the purpose behind resConfig? If so, how am I supposed to force a flavor to only run in only language and not be dependant on the OS locale setting? This solution must be compatible with flavorDimensions too since I use them in the real app I'm trying to configure.

NOTE: I also filed a bug since I think there really is a problem with this resConfig behavior https://code.google.com/p/android/issues/detail?id=180694

Thanks

Answer

Eric Labelle picture Eric Labelle · Aug 3, 2015

Well, after many hours, here's the solution to my problem for those ending up with the same problem.

Turns out that resConfig is not for what I though. The proper and only way I found to force the locale for a specific flavor is like this:

First, make sure all your activities extends a common activity which will be responsible to force the locale. (I had to create 2 since I have some activities extending FragmentActivity and some extending Activity. I also don't really like doing so in the Activities constructor but since I have to call applyOverrideConfiguration before any call is done on getResources, I need to call it before the activity's 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);      
}

Next, you need to define the locale in your 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.



Alternate solution (won't work on Android M+ see android bug report) This solution is wide spreaded on stackOverflow but I faced a wall while implementing this on Android M so keep this in mind.

In the onCreate() of your AndroidApplication you can call updateConfiguration with the locale set.

Locale myLocale = new Locale(BuildConfig.LOCALE);
Resources res = getResources();
Configuration conf = res.getConfiguration();
conf.locale = myLocale;
res.updateConfiguration(conf, res.getDisplayMetrics());

You will also need to reset it if you defined 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());
    }

EDIT: Be aware that this applyOverrideConfiguration currently has a side effect which is explained here Chromium webview does not seems to work with Android applyOverrideConfiguration. The Chromium team are currently working on it!

Thanks.