Localization — Best way to support several language

Discontinued

Since Google announced Android 13 with per-app language preferences supports. This feature also backport to older Android version with AndroidX. So there's no reason to contribute this library anymore. For more stability, compatibility, and longer supports from Google team, please migrate to AndroidX. See Migrate to AndroidX guide.

Localization/MIGRATE_TO_ANDROIDX.md at master · akexorcist/Localization
[Android] In-app language changing library. Contribute to akexorcist/Localization development by creating an account on GitHub.

As a developer, would know that Android application have to support multiple languages. Yeah! It’s very easy for android to handle it for you. It is done through “String Resource.” The only thing that you must do is simply preparing texts in different languages. The rest is handled by android system.

However, it is too smart. String Resource is automatically adjusted to the current device’s language. We cannot easily switch language on-the-fly.

So I created a new library specifically to handle android application language switching. It called “Localization”.

Demo

You can watch a short demo from YouTube

Or try it from Google Play

Demo App for Localization Libr - Apps on Google Play
Demo for Localization Library

Features

  • On-the-fly language switching.
  • Auto setup when activity was created.
  • Current language configuration will be saved to SharedPreference automatically.
  • Easy.

Usage

Add a dependency to your build.gradle

implementation 'com.akexorcist:localizationactivity:1.2.2'

Use custom application class in your project and add the LocalizationApplicationDelegate class like this.

import android.app.Application; 
import android.content.Context; 
import android.content.res.Configuration; 
import com.akexorcist.localizationactivity.core.LocalizationApplicationDelegate; 

public class CustomApplication extends Application { 
    LocalizationApplicationDelegate localizationDelegate = new LocalizationApplicationDelegate(this); 
    
    @Override
    protected void attachBaseContext(Context base) { 
        super.attachBaseContext(localizationDelegate.attachBaseContext(base)); 
    } 
    
    @Override 
    public void onConfigurationChanged(Configuration newConfig) { 
        super.onConfigurationChanged(newConfig); 
        localizationDelegate.onConfigurationChanged(this); } 
        
    @Override 
    public Context getApplicationContext() { 
        return localizationDelegate.getApplicationContext(super.getApplicationContext()); 
    } 
}

This is an example for your activity class.

import android.os.Bundle;
import android.view.View;
import com.akexorcist.localizationactivity.ui.LocalizationActivity;

public class MainActivity extends LocalizationActivity implements View.OnClickListener {
    @Override
    public void onCreate(Bundle savedInstanceState) { 
         super.onCreate(savedInstanceState); 
         setContentView(R.layout.activity_simple); 
         findViewById(R.id.btn_th).setOnClickListener(this); 
         findViewById(R.id.btn_en).setOnClickListener(this); 
     } 
     
     @Override public void onClick(View v) { 
         int id = v.getId(); 
         if (id == R.id.btn_en) { 
             setLanguage("en"); 
         } else if (id == R.id.btn_th) { 
             setLanguage("th"); 
         } 
     }
 }

In the example above, when the button is clicked , language will be switched accordingly either Thai or English. That’s It!

Then, you just add String Resources for English and Thai in the values and values-th.

Completed! Now your application supports multiple languages without much sweat.

Extend from AppCompatActivity

LocalizationActivity is extended from AppCompatActivity class. If you already use methods from AppCompatActivity. You do not have to refactor your codes!

Public Methods

The only big adjustment on your codes is the following 3 public methods.

void setLanguage(String language) 
void setLanguage(String language, String country) 
String getLanguage() 
void setDefaultLanguage(String language) 
void setDefaultLanguage(String language, String country)

setLanguage

Set the language that you needs to switch. The given string value will be used to setup Locale class later.

setLanguage("th") 
// Language : Thailand 

setLanguage("th", "TH") 
// Language : Thailand, Country : Thai 

setLanguage("en") 
// Language : English 

setLanguage("en", "GB") 
// Language : English, Country : Great Britain 

setLanguage("en", "US")
// Language : English, Country : United States

setLanguage(Locale.KOREA) 
// Language : Korean, Country : Korea 

setLanguage(Locale.KOREAN) 
// Language : Korean 

setLanguage(Locale.CANADA_FRENCH) 
// Language : French, Country : Canada

So you must determine the correct language for Locale class

getLanguage

Get current language. (Return to string locale)

setDefaultLanguage

Set default language if there is no language configuration. This interface is called only once in the first activity. It will be called before super.onCreate

@Override
public void onCreate(Bundle savedInstanceState) { 
    setDefaultLanguage(Locale.JAPAN.toString()); 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    /* ... */ 
}

and 2 optional override methods.

void onBeforeLocaleChanged()
void onAfterLocaleChanged()

This override method will be called then activity language was changed. If you need to know when language has change, just override these methods.

Change the language on every activity. Although already created.

You can switch languages in all activities, regardless of whether those activities were in the back stack.

Normal activity behavior has troubles with language switching.

If you switch language in the third activity, the previous two activities wouldn’t switch language to be the same as the third activity.

You will find no such problems with LocalizationActivity. All activities in backstack will get the just switched language, too.

You must handle your data through the instance state

When language was switched. Your activity will be recreated. If you have any data object, you should handle your data by save/restore instance state to retain your data or it will be lost. (Still, this is what you must do when your app supports portrait and landscape orientation.)

Therefore, you have to override onSaveInstance and onRestoreInstance and handle it.

import android.os.Bundle; 
import android.view.View; 
import com.akexorcist.localizationactivity.ui.LocalizationActivity; 

public class MainActivity extends LocalizationActivity implements View.OnClickListener { 
    @Override public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        // TODO Initial view and widget here 
        if (savedInstanceState == null) { 
            // TODO Activity first created 
        } else { 
            // TODO Activity recreated from screen orientation or change language 
        } 
    } 
    
    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
        super.onSaveInstanceState(outState); 
        // TODO Save instance here 
    } 
    
    @Override 
    protected void onRestoreInstanceState(Bundle savedInstanceState) { 
        // TODO Restore instance here 
        super.onRestoreInstanceState(savedInstanceState); 
    } 
}

Your fragment is affected as well.

Fragment language configuration depends on its hosted activity. If activity language was changed and recreated. Fragment will do so. You have to handle your data to Instance State on fragment just like the hosted activity.

For Save/Restore Instance State. Read more on The Real Best Practices to Save/Restore Activity’s and Fragment’s state.

The Real Best Practices to Save/Restore Activity’s and Fragment’s state. (StatedFragment is now deprecated)
Months ago I published an article related to Fragment State saving & restoring, Probably be the best way (?) to save/restore Android Fragment’s state so far. A lot of valuable feed

Activity Blinking Problem

It’s normal that the activity is blinking because it was recreated. I already fixed this problem by inserting a dummy activity to fade in/out transition while the language is switching in the latest version.

Don’t like AppCompat v7? Try the delegate way.

Sometimes your app doesn’t use AppCompatActivity from AppCompat v7 library. But you want to support multiple languages. You can create your own activity class and use LocalizationDelegate to make your activity class.

import android.app.Activity; 
import android.content.Context; 
import android.content.res.Resources; 
import android.os.Bundle; 
import com.akexorcist.localizationactivity.core.LocalizationActivityDelegate; 
import com.akexorcist.localizationactivity.core.OnLocaleChangedListener; 
import java.util.Locale; 

public abstract class CustomActivity extends Activity implements OnLocaleChangedListener { 
    private LocalizationActivityDelegate localizationDelegate = new LocalizationActivityDelegate(this); 
    
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        localizationDelegate.addOnLocaleChangedListener(this); 
        localizationDelegate.onCreate(savedInstanceState); 
        super.onCreate(savedInstanceState); 
    } 
    
    @Override
    public void onResume() { 
        super.onResume(); 
        localizationDelegate.onResume(this); 
    } 
    
    @Override 
    protected void attachBaseContext(Context newBase) { 
        super.attachBaseContext(localizationDelegate.attachBaseContext(newBase)); 
    } 
    
    @Override 
    public Context getApplicationContext() { 
        return localizationDelegate.getApplicationContext(super.getApplicationContext()); 
    } 
    
    @Override public Resources getResources() { 
        return localizationDelegate.getResources(super.getResources()); 
    } 
        
    public final void setLanguage(String language) { 
        localizationDelegate.setLanguage(this, language); 
    } 
    
    public final void setLanguage(Locale locale) { 
        localizationDelegate.setLanguage(this, locale); 
    } 
    
    public final void setDefaultLanguage(String language) { 
        localizationDelegate.setDefaultLanguage(language); 
    } 
    
    public final void setDefaultLanguage(Locale locale) { 
        localizationDelegate.setDefaultLanguage(locale); 
    } 
    
    public final Locale getCurrentLanguage() { 
        return localizationDelegate.getLanguage(this); 
    } 
    
    // Just override method locale change event 
    @Override 
    public void onBeforeLocaleChanged() { } 
    
    @Override 
    public void onAfterLocaleChanged() { } 
}

And don’t forget to exclude AppCompat v7 dependency from this library in build.gradle to reduce total method count.

implementation ('com.akexorcist:localizationactivity:+') { 
    exclude module: 'appcompat-v7' 
}

Examples

If you don’t understand how to use it. I have built an example project that contains 3 example codes and this library is open source. Hopefully it will help you understand it. See at Localization [GitHub]

GitHub - akexorcist/Localization: [Android] In-app language changing library
[Android] In-app language changing library. Contribute to akexorcist/Localization development by creating an account on GitHub.

About 6 example codes. It consists of

  • Simple Activity
  • Simple Activity that extends from Custom Localization Activity
  • Simple Activity with Change Language Activity
  • Activity + Fragment
  • Activity + Nested Fragment
  • Activity + View Pager

All three examples will handleSave/Restore Instance State. It supports the portrait/landscape orientation as well. (Layout is for demonstration only, it won’t be beautiful.)

Feel free to give any advice

It would be great if you can help me improve my English. Feel free to criticize my wrong grammar.