Force locale for Android flavor with resConfig
Asked Answered
T

2

19

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

Timpani answered 22/7, 2015 at 14:35 Comment(0)
T
15

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.

Timpani answered 3/8, 2015 at 12:24 Comment(2)
as an aside (since i am the one who filed the bug report you linked above), the alternate solution mentioned above does work on M if the context used is from an Activity and not from an Application.Swoop
Tried this solution for devices >= API level 17, works. Only problem I am concerned is setting values in the constructor of Activity which is like an anti-pattern for Android component initialization. Did you ever encounter any issue using this approach i.e. crash or bug ?Voile
B
6

resConfig does not override the specified resConfigs of the defaultConfig, but just combines them instead. Thus you'll end up with all flavors containing en and fr resources.

So, to avoid that, you shouldn't specify any resConfigs in the defaultConfig and just set it in the specific flavor.

Further, the app will always include the default values, which means if you have English strings there, the fr flavor will still show up with English content on a device set to any non-French locale.

Bosky answered 12/1, 2018 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.