I have a multilingual app with primary language English and secondary language Arabic.
I am calling setLocale()
in the onCreate()
of every Activity
in my app:
public static void setLocale(Locale locale){
Locale.setDefault(locale);
Context context = MyApplication.getInstance();
final Resources resources = context.getResources();
final Configuration config = resources.getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
resources.getDisplayMetrics());
}
where locale
is one of the following:
The above method is called before super.onCreate(savedInstanceState)
gets called.
As described in the documentation,
android:supportsRtl="true"
in the manifest.left
and right
attributes to start
and end
respectively.res\values-ar\strings
folder and drawable resources in res\drawable-ar
folder (and similarly for other resources).The above setup works properly. After changing the Locale
to ar-AE
, Arabic text & resources are correctly displayed in my Activities.
However, there is a problem with both resources and layout direction for all Android devices with version 8.0 and above.
On a device with version less than 8.0, an RTL screen correctly looks like this:
And on all devices with 8.0+, the same screen turns up looking like this:
which is wrong.
It turns out that both the direction and the resources are getting displayed incorrectly.
There are two problems here:
Locale
does not seem to be updated across the app configuration.With respect to direction, a curious method called setLayoutDirection()
exists which I had not noticed before.
I would like to know what this problem is, why it happens in Oreo and what is the solution for it. Please help / comment on this.
EDIT:
According to the API Differences report, the
updateConfiguration()
method was indeed deprecated in Android 7.1 (API level 25).
Also, found all the relevant posts on this. In order of importance:
1. Android N change language programmatically.
2. Android context.getResources.updateConfiguration() deprecated.
3. How to change Android O / Oreo / api 26 app language.
4. Android RTL issue in API 24 and higher on locale change
5. Change language programmatically (Android N 7.0 - API 24).
The complete solution to this problem consists of three steps:
STEP 1:
In the onCreate()
of your BaseActivity
(or all your Activity
s), set the Locale
as follows:
@Override
protected void onCreate(Bundle savedInstanceState) {
// set the Locale the very first thing
Utils.setLocale(Utils.getSavedLocale());
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
......
......
}
where getSavedLocale()
is the Locale
corresponding to the current region (this will be specific for your project ... ).
And the method Utils.setLocale(...)
is defined as follows:
public static void setLocale(Locale locale){
Context context = MyApplication.getInstance();
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
Locale.setDefault(locale);
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
// updateConfiguration(...) is deprecated in N
if (Build.VERSION.SDK_INT >= 25) {
context = context.getApplicationContext().createConfigurationContext(configuration);
context = context.createConfigurationContext(configuration);
}
context.getResources().updateConfiguration(configuration,
resources.getDisplayMetrics());
}
This sets the correct Locale
in every Activity
. This is enough for apps supporting API level 25. For API level 26 & above, STEP 2 and STEP 3 are also required.
STEP 2:
Override the following method in your BaseActivity
:
@Override
protected void attachBaseContext(Context newBase) {
newBase = Utils.getLanguageAwareContext(newBase);
super.attachBaseContext(newBase);
}
where the function getLanguageAwareContext(...)
is defined as follows:
public static Context getLanguageAwareContext(Context context){
Configuration configuration = context.getResources().getConfiguration();
Locale locale = getIntendedLocale();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
This, along with STEP 1, sets the correct Locale
in every Activity
of your app for API level 26 and above.
One more step, however, is required for setting the language direction correctly ...
STEP 3:
In the onCreate()
of your BaseActivity
, add the following code:
@Override
protected void onCreate(Bundle savedInstanceState) {
....
....
// yup, it's a legit bug ... :)
if (Build.VERSION.SDK_INT >= 26) {
getWindow().getDecorView().setLayoutDirection(Utils.isRTL()
? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
....
....
}
where the isRTL()
function is defined as follows:
public static boolean isRTL(){
return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
}
The above steps should take care of all issues (at least regarding setting the Locale
and text direction) on all extant versions of Android.