mukesh
mukesh

Reputation: 4140

How to change language of app when user selects language?

I want my app to support three languages Spanish,Portuguese & English. And give option to select language in app.I have made

1) 3 drawable folders drawable-es,drawable-pt,drawable.

2) 3 values folder values-es,values-pt,values.Change String.xml values according to languages.

I have imageView to select language.When click it menu open that consists option English,Spanish,Portuguese.

I set Locale inside app on option selection by this code

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.en:
             Locale locale = new Locale("en"); 
             Locale.setDefault(locale);
             Configuration config = new Configuration();
             config.locale = locale;
             getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
             Toast.makeText(this, "Locale in English !", Toast.LENGTH_LONG).show();
             break;

        case R.id.pt:
             Locale locale2 = new Locale("pt"); 
             Locale.setDefault(locale2);
             Configuration config2 = new Configuration();
             config2.locale = locale2;
             getBaseContext().getResources().updateConfiguration(config2, getBaseContext().getResources().getDisplayMetrics());

             Toast.makeText(this, "Locale in Portugal !", Toast.LENGTH_LONG).show();
             break;

        case R.id.es:
             Locale locale3 = new Locale("es"); 
             Locale.setDefault(locale3);
             Configuration config3 = new Configuration();
             config3.locale = locale3;
             getBaseContext().getResources().updateConfiguration(config3, getBaseContext().getResources().getDisplayMetrics());

             Toast.makeText(this, "Locale in Spain !", Toast.LENGTH_LONG).show();
             break;     
    }
    return super.onOptionsItemSelected(item);
}

I have declare in Manifest- android:configChanges="locale"

It work but it have some issue.

Problem:-

1)When language selected, screen that consists image of language selection not change but other screens are change.

2)After orientation change app restore language according to locale of phone.

Upvotes: 124

Views: 245091

Answers (12)

Sergey Emeliyanov
Sergey Emeliyanov

Reputation: 6961

Answers in this thread mostly are outdated and do not work. I've figured out that there is a more or less working solution, which is described in the Google doc. However it's quite complex and has downsides, so I'll just write down this step-by-step guide, which works for most Android versions to date:

(1) Create a file called res/xml/locales_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
   <locale android:name="en"/>
   <locale android:name="de"/>
<locale-config/>

(2) Add reference to this file to AndroidManifest.xml:

<manifest>
    ...
    <application
        ...
        android:localeConfig="@xml/locales_config">
    </application>
</manifest>

(3) Also, add a service to support Android 12 and below:

<application
  ...
  <service
    android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
    android:enabled="false"
    android:exported="false">
    <meta-data
      android:name="autoStoreLocales"
      android:value="true" />
  </service>
  ...
</application>

(4) Add languages to app/build.gradle:

android {
      defaultConfig {
          ...
          resConfigs("en", "de")
      }
  }

(5) Change language in code:

AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("de"))

To make this method work, you'll need ot ensure that following are true:

  • targetSdk 33 and minSdk 24 (not a big deal nowadays for most apps);
  • Your activities extend AppCompatActivity and have the AppCompat* theme;
  • AppCompatDelegate is available from implementation 'androidx.appcompat:appcompat:1.6.0'.

IMPORTANT: When accessing your String (or any other localized resource), make sure that you use Context of the Activity, and not of the Application. Strings (or other resources) you will get via. Application Context will not be affected by this method, so they will be in the system language!

Upvotes: 0

Kevin Kalathiya
Kevin Kalathiya

Reputation: 61

Try using below library:

implementation("com.github.YarikSOffice:lingver:1.3.0")

You can change language using this line:

Lingver.getInstance().setLocale(context, languageList[languageAdapter.selectedPos].code)

also restart your main activity on language Change.

Upvotes: 0

Shiwen Huang
Shiwen Huang

Reputation: 1

My experience: Changing language must be called before setContentView to make sure all text are changed

Upvotes: 0

Lokesh Tiwari
Lokesh Tiwari

Reputation: 10586

all the above @Uday's code is perfect but only one thing is missing(default config in build.gradle)

public void setLocale(String lang) { 
    Locale myLocale = new Locale(lang); 
    Resources res = getResources(); 
    DisplayMetrics dm = res.getDisplayMetrics(); 
    Configuration conf = res.getConfiguration(); 
    conf.locale = myLocale; 
    res.updateConfiguration(conf, dm); 
    Intent refresh = new Intent(this, AndroidLocalize.class); 
    finish();
    startActivity(refresh); 
} 

Mine was not working just because the languages were not mentioned in the config file(build.gradle)

 defaultConfig {
    resConfigs "en", "hi", "kn"
}

after that, all languages started running

Upvotes: 12

user19576538
user19576538

Reputation:

There's a new (and better) implementation for in-app language pickers since Appcompat 1.6.0-alpha04 and later: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl

Methods used in custom implementations suggested by others are deprecated in API level 25.

Upvotes: 2

Mrudul Tora
Mrudul Tora

Reputation: 768

    Locale locale = new Locale(langCode);
    Locale.setDefault(locale);
    Configuration configuration = context.getResources().getConfiguration();
    configuration.locale = locale;
    configuration.setLayoutDirection(locale); // for RTL changes
    preferences.setLocalePref(langCode);
    context.getResources().updateConfiguration(configuration, context.getResources().getDisplayMetrics());

Here, langCode is the required language code. You can save the language code as string in sharedPreferences. and you can call this code super.onCreate(savedInstanceState) in onCreate.

Upvotes: 0

Kotlin solution using context extension

fun Context.applyNewLocale(locale: Locale): Context {
    val config = this.resources.configuration
    val sysLocale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        config.locales.get(0)
    } else {
        //Legacy
        config.locale
    }
    if (sysLocale.language != locale.language) {
        Locale.setDefault(locale)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            config.setLocale(locale)
        } else {
            //Legacy
            config.locale = locale
        }
        resources.updateConfiguration(config, resources.displayMetrics)
    }
    return this
}

Usage

override fun attachBaseContext(newBase: Context?) {
    super.attachBaseContext(newBase?.applyNewLocale(Locale("es", "MX")))
}

Upvotes: 5

sud007
sud007

Reputation: 6141

Good solutions explained pretty well here. But Here is one more.

Create your own CustomContextWrapper class extending ContextWrapper and use it to change Locale setting for the complete application. Here is a GIST with usage.

And then call the CustomContextWrapper with saved locale identifier e.g. 'hi' for Hindi language in activity lifecycle method attachBaseContext. Usage here:

@Override
protected void attachBaseContext(Context newBase) {
    // fetch from shared preference also save the same when applying. Default here is en = English
    String language = MyPreferenceUtil.getInstance().getString("saved_locale", "en");
    super.attachBaseContext(MyContextWrapper.wrap(newBase, language));
}

Upvotes: 11

Udhay
Udhay

Reputation: 1946

It's excerpt for the webpage: http://android.programmerguru.com/android-localization-at-runtime/

It's simple to change the language of your app upon user selects it from list of languages. Have a method like below which accepts the locale as String (like 'en' for English, 'hi' for hindi), configure the locale for your App and refresh your current activity to reflect the change in language. The locale you applied will not be changed until you manually change it again.

public void setLocale(String lang) { 
    Locale myLocale = new Locale(lang); 
    Resources res = getResources(); 
    DisplayMetrics dm = res.getDisplayMetrics(); 
    Configuration conf = res.getConfiguration(); 
    conf.locale = myLocale; 
    res.updateConfiguration(conf, dm); 
    Intent refresh = new Intent(this, AndroidLocalize.class); 
    finish();
    startActivity(refresh); 
} 

Make sure you imported following packages:

import java.util.Locale; 
import android.os.Bundle; 
import android.app.Activity; 
import android.content.Intent; 
import android.content.res.Configuration; 
import android.content.res.Resources; 
import android.util.DisplayMetrics; 

add in manifest to activity android:configChanges="locale|orientation"

Upvotes: 188

Fisher
Fisher

Reputation: 509

Udhay's sample code works well. Except the question of Sofiane Hassaini and Chirag SolankI, for the re-entrance, it doesn't work. I try to call Udhay's code without restart the activity in onCreate() , before super.onCreate(savedInstanceState);. Then it is OK! Only a little problem, the menu strings still not changed to the set Locale.

    public void setLocale(String lang) { //call this in onCreate()
      Locale myLocale = new Locale(lang); 
      Resources res = getResources(); 
      DisplayMetrics dm = res.getDisplayMetrics(); 
      Configuration conf = res.getConfiguration(); 
      conf.locale = myLocale; 
      res.updateConfiguration(conf, dm); 
      //Intent refresh = new Intent(this, AndroidLocalize.class); 
      //startActivity(refresh); 
      //finish();
    } 

Upvotes: 3

Wahab Khan Jadon
Wahab Khan Jadon

Reputation: 1176

Those who getting the version issue try this code ..

public static void switchLocal(Context context, String lcode, Activity activity) {
        if (lcode.equalsIgnoreCase(""))
            return;
        Resources resources = context.getResources();
        Locale locale = new Locale(lcode);
        Locale.setDefault(locale);
        android.content.res.Configuration config = new 
        android.content.res.Configuration();
        config.locale = locale;
        resources.updateConfiguration(config, resources.getDisplayMetrics());
        //restart base activity 
        activity.finish();
        activity.startActivity(activity.getIntent());
    }

Upvotes: 3

Frane Poljak
Frane Poljak

Reputation: 2365

You should either remove android:configChanges="locale" from manifest, which will cause activity to reload, or override onConfigurationChanged method:

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
    // your code here, you can use newConfig.locale if you need to check the language
    // or just re-set all the labels to desired string resource
}

Upvotes: 6

Related Questions