Friday, March 25, 2011

Android force locale and orientation problem

In my attemps to force locale in my application, i've faced an orientation problem.
Forcing locale worked, but when the orientation was changed, language would go back to default.
I spent some time resolving this issue, so I've decided to share my solution.

They way to force locale in your application is to extend Application.
You create a class that extends Application class and set it up for use in AndroidManifest.xml

The code i used in my application is this:

public class MyApplication extends Application
{
    public static final String FORCE_LOCAL = "force_local";
    public static final String FORCE_CURRENCY = "force_currency";
   
  @Override
  public void onCreate()
  {
    updateLanguage(this,null);
    super.onCreate();
  }

  public static void updateLanguage(Context ctx, String lang)
  {
    Configuration cfg = new Configuration();
    SharedPreferences force_pref = PreferenceManager.getDefaultSharedPreferences(ctx);
    String language = force_pref.getString(FORCE_LOCAL, "");
   
    if(TextUtils.isEmpty(language)&&lang==null){
        cfg.locale = Locale.getDefault();

        SharedPreferences.Editor edit = force_pref.edit();
        String tmp="";
        tmp=Locale.getDefault().toString().substring(0, 2);
       
        edit.putString(FORCE_LOCAL, tmp);
        edit.commit();
       
    }else if(lang!=null){
        cfg.locale = new Locale(lang);
        SharedPreferences.Editor edit = force_pref.edit();
        edit.putString(FORCE_LOCAL, lang);
        edit.commit();
       
    }else if(!TextUtils.isEmpty(language)){
      cfg.locale = new Locale(language);
    }
   
    ctx.getResources().updateConfiguration(cfg, null);
  }
 
 
  @Override
  public void onConfigurationChanged(Configuration newConfig)
  {
        SharedPreferences force_pref = PreferenceManager.getDefaultSharedPreferences(getBaseContext().getApplicationContext());

      String language = force_pref.getString(FORCE_LOCAL, "");    
       
      super.onConfigurationChanged(newConfig);
      updateLanguage(this,language);
  }
 
}

In the AndroidManifest.xml you should add the following attribute:

<application android:name="janalysis.pocetni.MyApplication" ...

Using onConfigurationChanged(Configuration newConfig) function where updateLanguage(this,language); is called solves the orientation problem.

onConfigurationChanged(Configuration newConfig) is called every time orientation changes from Portrait to Landscape or the other way around.

To change locale from your application you use
MyApplication.updateLanguage(getApplicationContext(), "en");

You use the string of a language you want to force instead of „en“,
for exaple „de“ for German, „fr“ for French, „hr“ for Croatioan..

After using this piece of code in your application only activities loaded after the change has been made will be translated into new language.
Activities that are already open will be displayed with the old language.

The way I solved it is restarting application after updating language. Restarting can be done using PackageManager, like this:

Intent i = getBaseContext()
.getPackageManager()
.getLaunchIntentForPackage(getBaseContext()
.getPackageName());
                               
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

startActivity(i);

It would be nice to let the user know application will/needs to restart. You can use, for example, an AlertDialog.. it's up to you.

After installing and first run, you app will be set up to a locale used on the phone. If yout app does not support phone's local it will use the default one.

After changing the locale within the app itself, that locale will be saved, and app will use it until you change it again within the app itself.

That means that after you've set up you locale in the app, if you change phone's locale, app will still keep it's locale.

You can test your application's behaviour on a device using free apps for forcing locales. These can be found on Market: 'More Locale 2' or 'LocaleSwitch' for example..

Hope this helps ;-)
Ivan