Android & Kotlin

Android Multiple Language Support Best Practices

Pinterest LinkedIn Tumblr

Welcome, Buddy… In this tutorial, we demonstrate, How to Build multi-language support apps in the android application. As per android best practice, Application should be kept culture-specific resource. So that resource to be translated to the language based on current locale. In android term, it is called Internationalization and Localization

Localization is play very important role for app reach and user experience (especially GUI). Multi-language support increases your audience reach and provides a better user interface based on locale. Let’s make your Android app more usable and comfortable so more and more audience can use it

How do Internationalization and Localization work in android?

In, Android you can specify resources to the culture of the targeted audience. All content related resource is held in drawable and value folder in Android Project. In this tutorials as will give support of 3 languages Spanish, Hindi (hi_IN)and English(en_US)

Multi Language Supported App in Android (Sample App)

1. Let’s build a sample app in 4 step

1.1 Create a new Project with the following details

In Android Studio, Go to File Menu -> New Project -> Fill some essential details like application name and choose androidx -> Select target SDK -> Select BasicActivity template -> Finish

1.2 Add new resource file strings.xml for the Hindi language

Go to value res folder and right click on go to the value resource file. Select locale from available qualifier clicks the right arrow. Select language and specific region (the region is not mandatory). Check below figure

multi language app android example


Similarly, create the file for Spanish and English locale. So you have to create three locale file for Hindi, English, and Spanish languages

Create a string entry for English locale

<resources>
  <string name="app_name">Multi Language App</string>
  <string name="navigation_drawer_open">Open navigation drawer</string>
  <string name="navigation_drawer_close">Close navigation drawer</string>
  <string name="nav_header_title">Android Wave</string>
  <string name="nav_header_subtitle">androidwave.com</string>
  <string name="nav_header_desc">Android & iOS Developer Blog</string>
  <string name="action_settings">Settings</string>
  <string name="description_androidwave">We demonstrate android & iOS app development tutorials for
    Firebase, Camera2 API, Exo Player, Youtube API, Dagger2, RxJava, MVVM, MVP, Realm, and more.</string>
  <string name="text_hindi" translatable="false">हिंदी</string>
  <string name="text_english" translatable="false">English</string>
  <string name="text_spanish" translatable="false">" Español"</string>

  <!-- Navigation Menu-->
  <string name="title_import">Import</string>
  <string name="title_gallery">Gallery</string>
  <string name="title_slideshow">Slideshow</string>
  <string name="title_tools">Tools</string>
  <string name="title_communication">Communicate</string>
  <string name="title_share">Share</string>
  <string name="title_send">Send</string>
</resources>

The same key should be in Hindi locale

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">बहु भाषा ऐप</string>
  <string name="navigation_drawer_open">नेविगेशन ड्रावर को खोले</string>
  <string name="navigation_drawer_close">नेविगेशन ड्रावर को बंद करें </string>
  <string name="nav_header_title">एंड्रॉइड वेव</string>
  <string name="nav_header_subtitle">androidwave.com</string>
  <string name="nav_header_desc">एंड्रॉइड और आईओएस डेवलपर ब्लॉग</string>
  <string name="action_settings">सेटिंग</string>
  <string name="description_androidwave">हम फायरबेस, कैमरा 2 एपीआई, एक्सो प्लेयर, यूट्यूब एपीआई, डैगर 2, आरएक्सजेवा,
    एमवीवीएम, एमवीपी, दायरे, आदि के लिए एंड्रॉइड और आईओएस ऐप डेवलपमेंट ट्यूटोरियल का प्रदर्शन करते हैं।</string>
  <!-- Navigation Menu-->
  <string name="title_import">आयात</string>
  <string name="title_gallery">गैलरी</string>
  <string name="title_slideshow">स्लाइड शो</string>
  <string name="title_tools">उपकरण</string>
  <string name="title_communication">संवाद</string>
  <string name="title_share">शेयर</string>
  <string name="title_send">भेजना</string>
</resources>

Furthermore, have to for Spanish

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">Aplicación multi idioma</string>
  <string name="navigation_drawer_open">Cajón de navegación abierto.</string>
  <string name="navigation_drawer_close">Cerrar el cajon de navegacion</string>
  <string name="nav_header_title">Android Wave</string>
  <string name="nav_header_subtitle">androidwave.com</string>
  <string name="nav_header_desc">Android & Blog del desarrollador de iOS</string>
  <string name="action_settings">Ajustes</string>
  <string name="description_androidwave">Demostramos android & Tutoriales de desarrollo de aplicaciones
    iOS para Firebase, Camera2 API, Exo Player, Youtube API, Dagger2, RxJava, MVVM, MVP, Realm y más</string>
  <!-- Navigation Menu-->
  <string name="title_import">Importar</string>
  <string name="title_gallery">Galería</string>
  <string name="title_slideshow">Diapositivas</string>
  <string name="title_tools">Herramientas</string>
  <string name="title_communication">Comunicar</string>
  <string name="title_share">Compartir</string>
  <string name="title_send">Enviar</string>
</resources>
The complete project hierarchy looks like this figure
android example

3. Now build LocalManager.java utilities.

3.1. Define some specific constant and define all support locale in LocaleDef annotated interface
  @Retention(RetentionPolicy.SOURCE)
  @StringDef({ ENGLISH, HINDI, SPANISH })
  public @interface LocaleDef {
    String[] SUPPORTED_LOCALES = { ENGLISH, HINDI, SPANISH };
  }

  static final String ENGLISH = "en";
  static final String HINDI = "hi";
  static final String SPANISH = "es";
3.2. Save locale preference in PreferenceManager

Write getter and setter for prefs, It’s setter responsible for persisting data in SharedPreferences and getter are responsible for pulling data from SharedPreferences.

  /**
   * Get saved Locale from SharedPreferences
   *
   * @param mContext current context
   * @return current locale key by default return english locale
   */
  public static String getLanguagePref(Context mContext) {
    SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
    return mPreferences.getString(LANGUAGE_KEY, ENGLISH);
  }

  /**
   * set pref key
   */
  private static void setLanguagePref(Context mContext, String localeKey) {
    SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
    mPreferences.edit().putString(LANGUAGE_KEY, localeKey).apply();
  }
Now set locale and update the resource
  /**
   * set current pref locale
   */
  public static Context setLocale(Context mContext) {
    return updateResources(mContext, getLanguagePref(mContext));
  }

  /**
   * Set new Locale with context
   */
  public static Context setNewLocale(Context mContext, @LocaleDef String language) {
    setLanguagePref(mContext, language);
    return updateResources(mContext, language);
  }

  /**
   * update resource
   */
  private static Context updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);
    Resources res = context.getResources();
    Configuration config = new Configuration(res.getConfiguration());
    if (Build.VERSION.SDK_INT >= 17) {
      config.setLocale(locale);
      context = context.createConfigurationContext(config);
    } else {
      config.locale = locale;
      res.updateConfiguration(config, res.getDisplayMetrics());
    }
    return context;
  }

Finally, the complete LocaleManager class looks like below

package com.androidwave.multilanguage;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import androidx.annotation.StringDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;

public class LocaleManager {

  @Retention(RetentionPolicy.SOURCE)
  @StringDef({ ENGLISH, HINDI, SPANISH })
  public @interface LocaleDef {
    String[] SUPPORTED_LOCALES = { ENGLISH, HINDI, SPANISH };
  }

  static final String ENGLISH = "en";
  static final String HINDI = "hi";
  static final String SPANISH = "es";

  /**
   * SharedPreferences Key
   */
  private static final String LANGUAGE_KEY = "language_key";

  /**
   * set current pref locale
   */
  public static Context setLocale(Context mContext) {
    return updateResources(mContext, getLanguagePref(mContext));
  }

  /**
   * Set new Locale with context
   */
  public static Context setNewLocale(Context mContext, @LocaleDef String language) {
    setLanguagePref(mContext, language);
    return updateResources(mContext, language);
  }

  /**
   * Get saved Locale from SharedPreferences
   *
   * @param mContext current context
   * @return current locale key by default return english locale
   */
  public static String getLanguagePref(Context mContext) {
    SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
    return mPreferences.getString(LANGUAGE_KEY, ENGLISH);
  }

  /**
   * set pref key
   */
  private static void setLanguagePref(Context mContext, String localeKey) {
    SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
    mPreferences.edit().putString(LANGUAGE_KEY, localeKey).apply();
  }

  /**
   * update resource
   */
  private static Context updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);
    Resources res = context.getResources();
    Configuration config = new Configuration(res.getConfiguration());
    if (Build.VERSION.SDK_INT >= 17) {
      config.setLocale(locale);
      context = context.createConfigurationContext(config);
    } else {
      config.locale = locale;
      res.updateConfiguration(config, res.getDisplayMetrics());
    }
    return context;
  }

  /**
   * get current locale
   */
  public static Locale getLocale(Resources res) {
    Configuration config = res.getConfiguration();
    return Build.VERSION.SDK_INT >= 24 ? config.getLocales().get(0) : config.locale;
  }
}

5. Locale Configuration

Few minor configurations have to need in every activity and application class. For that, I’m creating a base class named is BaseActivity. In this activity, I have written methods for activity title and updating the LocaleManager context.

package com.androidwave.multilanguage;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import static android.content.pm.PackageManager.GET_META_DATA;

public abstract class BaseActivity extends AppCompatActivity {

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    resetTitles();
  }

  @Override
  protected void attachBaseContext(Context base) {
    super.attachBaseContext(LocaleManager.setLocale(base));
  }

  protected void resetTitles() {
    try {
      ActivityInfo info = getPackageManager().getActivityInfo(getComponentName(), GET_META_DATA);
      if (info.labelRes != 0) {
        setTitle(info.labelRes);
      }
    } catch (PackageManager.NameNotFoundException e) {
      e.printStackTrace();
    }
  }
}

6. Implement Locale Configuration in the Application class

Same things I have to in Application class. Let’s see below code, here we also settings locale on onConfigurationChanged and attachBaseContext.

package com.androidwave.multilanguage;

import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;

public class MultiLanguageApp extends Application {

  @Override
  protected void attachBaseContext(Context base) {
    super.attachBaseContext(LocaleManager.setLocale(base));
  }

  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    LocaleManager.setLocale(this);
  }
}

Above all locale configuration is complete and LocaleManager utilities are ready to use. In conclusion, just call below method for change locale of the application

  private void setNewLocale(AppCompatActivity mContext, @LocaleManager.LocaleDef String language) {
    LocaleManager.setNewLocale(this, language);
    Intent intent = mContext.getIntent();
    startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
  }
  // En

Implement localization in your android application

Now open the Activity class where you want to change the app language of the app. In application is we mostly written in SettingActivity. But in this android app tutorial we writing in MainActvitiy. Let’s open MainActvitiy and paste below code

package com.androidwave.multilanguage;

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;

public class MainActivity extends BaseActivity
    implements NavigationView.OnNavigationItemSelectedListener {
  DrawerLayout drawerLayout;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
            .setAction("Action", null).show();
      }
    });

    drawerLayout = findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
        this, drawerLayout, toolbar, R.string.navigation_drawer_open,
        R.string.navigation_drawer_close);
    drawerLayout.addDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);
  }

  @Override
  public void onBackPressed() {
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
      drawerLayout.closeDrawer(GravityCompat.START);
    } else {
      super.onBackPressed();
    }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    switch (id) {
      case R.id.local_english:
        setNewLocale(this, LocaleManager.ENGLISH);
        return true;
      case R.id.local_hindi:
        setNewLocale(this, LocaleManager.HINDI);
        return true;

      case R.id.local_spanish:
        setNewLocale(this, LocaleManager.SPANISH);
        return true;
    }

    return super.onOptionsItemSelected(item);
  }

  private void setNewLocale(AppCompatActivity mContext, @LocaleManager.LocaleDef String language) {
    LocaleManager.setNewLocale(this, language);
    Intent intent = mContext.getIntent();
    startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
  }

  @SuppressWarnings("StatementWithEmptyBody")
  @Override
  public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();
    switch (id) {
      case R.id.nav_camera:
        break;
      case R.id.nav_gallery:
        break;
      case R.id.nav_slideshow:
        break;
      case R.id.nav_manage:
        break;
      case R.id.nav_share:
        break;
      case R.id.nav_send:
        break;

      default:
        break;
    }
    drawerLayout.closeDrawer(GravityCompat.START);
    return true;
  }
}

Finally, run the project and change your language. You will get the output as shown in the video.

Conclusion

Thank’s for reading, In this tutorial, we have learned implementation Android Multi Language App Example. I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.

Keep in touch

If you have any queries, feel free to connect us. Read recent blog Why TypeScript is better than JavaScript

Download Sample Project- Android Multi Language App Example

20 Comments

  1. Japhungshar Brahma Reply

    Can you suggest as how to approach building a multi language android application which supports minor language that is not supported/included in android studio.

  2. patruni murty Reply

    nice tutorial, I am looking for. Android documentation explains how to support multiple languages and internationalization. That expects the user to change the locale from the setting or take the user to the settings from the app. In either case the changes apply across all apps, which the user may not intent to do. My requirement is to allow the user to change the language specific to my app and this full fills it.

    Is there a tutorial to create voice form the text or play pre-recoded voice files for different languages.

  3. How to save that language until user uninstall the application..?

  4. Huzaifa Butt Reply

    Thanks a lot for this. I tried it all but still language was not changing in older phones (API <= 24). The locale of application was changing but not the activity. After a lot a searching I found out that I also have to override applyOverrideConfiguration in BaseActivty.
    Like this:

    @Override
    public void applyOverrideConfiguration(Configuration overrideConfiguration) {
    Locale locale = new Locale(LocaleManager.getLanguagePref(this));
    Locale.setDefault(locale);
    overrideConfiguration.setLocale(locale);
    super.applyOverrideConfiguration(overrideConfiguration);
    }

  5. Arman Niazi Reply

    Hi Dear Sir
    Hope you going well!

    this Multiple Language Support demo is only in API 24 and Higher.
    how to do it to work with API 18 and higher

    • Tauseef ur Rehman

      have you got any solution for 18 please share with me.

    • Steven Ho

      I modified the below version judgement from 17 to Build.VERSION_CODES.N.
      And have tested the code on Android version 4.4 \ 6 \ 8 and it works for me !

      private static Context updateResources(Context context, String language) {
      Locale locale = new Locale(language);
      Locale.setDefault(locale);
      Resources res = context.getResources();
      Configuration config = new Configuration(res.getConfiguration());
      //if (Build.VERSION.SDK_INT >= 17) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      config.setLocale(locale);
      context = context.createConfigurationContext(config);
      } else {
      config.locale = locale;
      res.updateConfiguration(config, res.getDisplayMetrics());
      }
      return context;
      }

  6. I am developing a personal app, in that I want the user’s to select the language for the App ( Like Paytm ), at the time when app launches. I will show the list of languages in the Recyclerview, and selected language will be applied throughout the App. This is my requirement. Please suggest

  7. This will change the language in the RecyclerView List view and Navigation items. But when an item on the list is clicked and goes to a view-pager that holds multiple fragments. changes wont apply. Can you please help with this?

    • Sorry, I dont have a facebook account; this is also a good place for other people which might face the same issue in the future.

  8. Nice Tutorial I have implemented hind and english its working find but when uploaded in playstore HINDI not working ,Please help me???

  9. Hi,
    I have been following your tutorials but i have a question is there a way to change language without recreating activity.

    • @Raju you can do, but title will not change.

    • AJ, You have to extend BaseActivity instance ofAppCompectActivity and change language like below
      // For English
      LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_ENGLISH);
      // For Hindi
      LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_HINDI);
      // For Spanish
      LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_SPANISH);

Write A Comment