How to refresh activity after changing language (Locale) inside application

Android

Android Problem Overview


My application users can change the language from the app's settings. Is it possible to change the language inside the application without having effect to general language settings ? This question of stackoverflow is very useful to me and i have tried it. After changing language newly created activities display with changed new language, but current activity and previously created activities which are in pause state are not updated.How to update activities ? I have also spent a lot of time trying to make the preference change to be applied immediately but didn't succeed. When application is restarted, all activities created again, so now language changed correctly.

android:configChanges="locale" 

also added in manifest for all activities. and also support all screen. Currently I have not done any thing in activity's onResume() method. Is there any way to refresh or update activity (without finish and starting again) ? Am I missing something to do in onResume() method?

Android Solutions


Solution 1 - Android

> After changing language newly created activities display with changed new language, but current activity and previously created activities which are in pause state are not updated.How to update activities ?

Pre API 11 (Honeycomb), the simplest way to make the existing activities to be displayed in new language is to restart it. In this way you don't bother to reload each resources by yourself.

private void restartActivity() {
	Intent intent = getIntent();
	finish();
	startActivity(intent);
}

Register an OnSharedPreferenceChangeListener, in its onShredPreferenceChanged(), invoke restartActivity() if language preference was changed. In my example, only the PreferenceActivity is restarted, but you should be able to restart other activities on activity resume by setting a flag.

Update (thanks @stackunderflow): As of API 11 (Honeycomb) you should use recreate() instead of restartActivity().

public class PreferenceActivity extends android.preference.PreferenceActivity implements
		OnSharedPreferenceChangeListener {

	// ...

	@Override
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
		if (key.equals("pref_language")) {
			((Application) getApplication()).setLocale();
			restartActivity();
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.preferences);
		getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
	}

	@Override
	protected void onStop() {
		super.onStop();
		getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
	}
}

I have a blog post on this topic with more detail, but it's in Chinese. The full source code is on github: PreferenceActivity.java

Solution 2 - Android

If I imagined that you set android:configChanges in manifest.xml and create several directory for several language such as: values-fr OR values-nl, I could suggest this code(In Activity class):

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    Button btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// change language by onclick a button
		     Configuration newConfig = new Configuration();
		     newConfig.locale = Locale.FRENCH;
		     onConfigurationChanged(newConfig);
		}
	});
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
    setContentView(R.layout.main);
    setTitle(R.string.app_name);
    
    // Checks the active language
    if (newConfig.locale == Locale.ENGLISH) {
        Toast.makeText(this, "English", Toast.LENGTH_SHORT).show();
    } else if (newConfig.locale == Locale.FRENCH){
        Toast.makeText(this, "French", Toast.LENGTH_SHORT).show();
    }
}

I tested this code, It is correct.

Solution 3 - Android

Since the string resources will have already been loaded for the existing locale, the activities already opened will not automatically display using strings from the new locale. The only way to solve this is to reload all of the strings and set them again on the views. Typically, a call to setContentView(...) will be able to cover this (depending on your Activity structure), but of course it has the side-effect of losing any view state you had.

public void onResume() {
    super.onResume();
    ...
    if (localeHasChanged) {
        setContentView(R.layout.xxx);
    }
    ...
}

You will probably not want to reload the views every single time in onResume(), but only when the locale has changed. Checking when to update the views (i.e. localeHasChanged) is a matter of propagating the locale change event to the previous activities. This could be done in many ways, such as using static singleton-esque state or persisting this event to storage.

You could also try to minimise the number of Activities that can be opened when you can change the locale, e.g. by having the selection be at one of the initial screens.

Solution 4 - Android

For Android 4.2 (API 17), you need to use android:configChanges="locale|layoutDirection" in your AndroidManifest.xml. See https://stackoverflow.com/questions/13856229/onconfigurationchanged-is-not-called-over-jellybean4-2-1

Solution 5 - Android

You can use recreate(); to restart your activity when Language change.

I am using following code to restart activity when language change:

SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
Configuration config = getBaseContext().getResources().getConfiguration();

String lang = settings.getString("lang_list", "");

if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang)) {
      recreate();  //this is used for recreate activity
      Locale locale = new Locale(lang);
      Locale.setDefault(locale);
      config.locale = locale;
      getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
}

Solution 6 - Android

The way we have done it was using Broadcasts:

  1. Send the broadcast every time the user changes language
  2. Register the broadcast receiver in the AppActivity.onCreate() and unregister in AppActivity.onDestroy()
  3. In BroadcastReceiver.onReceive() just restart the activity.

AppActivity is the parent activity which all other activities subclass.


Below is the snippet from my code, not tested outside the project, but should give you a nice idea.

When the user changes the language

sendBroadcast(new Intent("Language.changed"));

And in the parent activity

public class AppActivity extends Activity {

	/**
	 * The receiver that will handle the change of the language.
	 */
	private BroadcastReceiver mLangaugeChangedReceiver;

	@Override
	protected void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// ...
		// Other code here
		// ...

		// Define receiver
		mLangaugeChangedReceiver = new BroadcastReceiver() {

			@Override
			public void onReceive(final Context context, final Intent intent) {
				startActivity(getIntent());
				finish();
			}
		};

		// Register receiver
		registerReceiver(mLangaugeChangedReceiver, new IntentFilter("Language.changed"));
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
 
		// ...
		// Other cleanup code here
		// ...

		// Unregister receiver
		if (mLangaugeChangedReceiver != null) {
			try {
				unregisterReceiver(mLangaugeChangedReceiver);
				mLangaugeChangedReceiver = null;
			} catch (final Exception e) {}
		}
	}
}

This will also refresh the activity which changed the language (if it subclasses the above activity).

This will make you lose any data, but if it is important you should already have taken care of this using Actvity.onSaveInstanceState() and Actvity.onRestoreInstanceState() (or similar).

Let me know your thoughts about this.

Cheers!

Solution 7 - Android

Step by Step Approach Complete Approach which will work on all API level device.

Using this apporach, It will work without Recreating the existing Activity on screen

  1. Use Base Activity for attachBaseContext to set the locale language and extend this activity for all activities

    open class  BaseAppCompactActivity() : AppCompatActivity() {
      override fun attachBaseContext(newBase: Context) {
          super.attachBaseContext(LocaleHelper.onAttach(newBase))    
      }
    }
    
  2. Use Application attachBaseContext and onConfigurationChanged to set the locale language

    public class MyApplication extends Application {
    
      /**
       * overide to change local sothat language can be chnaged from android device  nogaut and above
       */
      @Override
      protected void attachBaseContext(Context base) {
          super.attachBaseContext(LocaleHelper.INSTANCE.onAttach(base));
      }
    
      @Override
      public void onConfigurationChanged(Configuration newConfig) {
          setLanguageFromNewConfig(newConfig);
          super.onConfigurationChanged(newConfig);
      }
    
      /*** also handle chnage  language if  device language chnaged **/
      private void setLanguageFromNewConfig(Configuration newConfig){
          Prefs.putSaveLocaleLanguage(this,  selectedLocaleLanguage );
          LocaleHelper.INSTANCE.onAttach(this);
      }
    }
    
  3. Use Locale Helper for handling language changes, this approach work on all device

    object LocaleHelper {
      private var defaultLanguage  :String = KycUtility.KYC_LANGUAGE.ENGLISH.languageCode
    
      fun onAttach(context: Context, defaultLanguage: String): Context {
          return setLocale(context, defaultLanguage)
      }
    
    
    
      fun setLocale(context: Context, language: String): Context {
          return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
              updateResources(context, language)
          } else updateResourcesLegacy(context, language)
    
      }
    
    
      @TargetApi(Build.VERSION_CODES.N)
      private fun updateResources(context: Context, language: String): Context {
          val locale = Locale(language)
          Locale.setDefault(locale)
          val configuration = context.getResources().getConfiguration()
          configuration.setLocale(locale)
          configuration.setLayoutDirection(locale)
          return context.createConfigurationContext(configuration)
      }
    
      private fun updateResourcesLegacy(context: Context, language: String): Context {
          val locale = Locale(language)
          Locale.setDefault(locale)
          val resources = context.getResources()
          val configuration = resources.getConfiguration()
          configuration.locale = locale
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
              configuration.setLayoutDirection(locale)
          }
          resources.updateConfiguration(configuration, resources.getDisplayMetrics())
          return context
      }
    }
    

Solution 8 - Android

I solved my problem with this code

public void setLocale(String lang) {

		myLocale = new Locale(lang);
		Resources res = getResources();
		DisplayMetrics dm = res.getDisplayMetrics();
		Configuration conf = res.getConfiguration();
		conf.locale = myLocale;
		res.updateConfiguration(conf, dm);
		 
		onConfigurationChanged(conf);
	}
	
	
	
	@Override
	public void onConfigurationChanged(Configuration newConfig) 
	{
		iv.setImageDrawable(getResources().getDrawable(R.drawable.keyboard));
	    greet.setText(R.string.greet);
	    textView1.setText(R.string.langselection);

	    super.onConfigurationChanged(newConfig);
	    
	}

Solution 9 - Android

Call this method to change app locale:

public void settingLocale(Context context, String language) {

	Locale locale;

	Configuration config = new Configuration();

	 if(language.equals(LANGUAGE_ENGLISH)) {

		locale = new Locale("en");

		Locale.setDefault(locale);

		config.locale = locale;

	}else if(language.equals(LANGUAGE_ARABIC)){

		locale = new Locale("hi");

		Locale.setDefault(locale);

		config.locale = locale;

	}

	context.getResources().updateConfiguration(config, null);

    // Here again set the text on view to reflect locale change

    // and it will pick resource from new locale

    tv1.setText(R.string.one); //tv1 is textview in my activity

}

Note: Put your strings in value and values- folder.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKhushbu ShahView Question on Stackoverflow
Solution 1 - AndroidaleungView Answer on Stackoverflow
Solution 2 - AndroidOmid NazifiView Answer on Stackoverflow
Solution 3 - AndroidantonytView Answer on Stackoverflow
Solution 4 - Androiduser2129417View Answer on Stackoverflow
Solution 5 - AndroidAbhishek T.View Answer on Stackoverflow
Solution 6 - AndroidSaboView Answer on Stackoverflow
Solution 7 - AndroidmaniView Answer on Stackoverflow
Solution 8 - AndroidDakshesh KhatriView Answer on Stackoverflow
Solution 9 - AndroidubhusriView Answer on Stackoverflow