Introduction

This tutorial will explain you how to change your application language at run time (programatically).

In certain cases your Android Application is supposed to support multiple languages for example English, Arabic, French. This concept is known as Localization.

Developer design app layout in such a way that layout support Right to Left (rtl) support and Left to Right (ltr) support.

By default all Android apps support LTR Layouts.

We will be using Kotlin Programming Language. Which most of other articles are not using 🙂

Steps for Localizing an Android App:
  1. Create separate strings.xml files for each language supported in your Android App and add required strings in both files.
  2. Design XML Layout files using marginStart, marginEnd properties along with marginLeft and marginRight.
  3. Create LocaleManager Class which will be handling all functions related to localization/ language change.
  4. Create Activity Class from where we will update app language during runtime.
  5. Create Shared preferences helper class. This class will update/retrieve user language preference in shared preferences.
Adding strings in values/strings (Default English)
<resources>

<string name="meter_number">Meter Number</string>
<string name="meter_type">Meter Type</string>
<string name="howto_read_meter"><u>How do I read my meter?</u></string>
<string name="meter_last_reading_date">Last Reading Date</string>
<string name="meter_last_reading_count">Last Reading</string>

</resources>
Arabic strings.xml
<string name="meter_number">رقم  المرفق</string>
<string name="meter_type">نوع العداد</string>
<string name="howto_read_meter">كيف اقرا  العداد الخاص بي ؟</string>
<string name="meter_last_reading_date">تاريخ اخر قراءة</string>
<string name="meter_last_reading_count">اخر قراءة</string>

Add this flag in manifest under Application Tag.

android:supportsRtl="true"

//  MANIFEST.XML

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.developine.translation">

<application
android:allowBackup="true"
android:name=".Home"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Lets First Implemet Shared Preferences in Kotlin Way

Create a new File in your project and name PreferenceHelper

// PreferenceHelper.kt

import android.content.Context
import android.content.SharedPreferences
import android.preference.PreferenceManager

object PreferenceHelper {

    fun defaultPrefs(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)

    fun customPrefs(context: Context, name: String): SharedPreferences = context.getSharedPreferences(name, Context.MODE_PRIVATE)

    inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
        val editor = this.edit()
        operation(editor)
        editor.apply()
    }

    /**
     * puts a key value pair in shared prefs if doesn't exists, otherwise updates value on given [key]
     */
    operator fun SharedPreferences.set(key: String?, value: Any?) {
        when (value) {
            is String? -> edit({ it.putString(key, value) })
            is Int -> edit({ it.putInt(key, value) })
            is Boolean -> edit({ it.putBoolean(key, value) })
            is Float -> edit({ it.putFloat(key, value) })
            is Long -> edit({ it.putLong(key, value) })
            else -> throw UnsupportedOperationException("Not yet implemented")
        }
    }

    /**
     * finds value on given key.
     * [T] is the type of value
     * @param defaultValue optional default value - will take null for strings, false for bool and -1 for numeric values if [defaultValue] is not specified
     */
    operator inline fun <reified T : Any> SharedPreferences.get(key: String?, defaultValue: T? = null): T? {
        return when (T::class) {
            String::class -> getString(key, defaultValue as? String) as T?
            Int::class -> getInt(key, defaultValue as? Int ?: -1) as T?
            Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
            Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
            Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
            else -> throw UnsupportedOperationException("Not yet implemented")
        }
    }
}
Locale Manage Utility Class

We will be using Configuration, Resources and Locale Classes.

Configuration will have desired Locale and Resources will have  updated Configuration.

import android.content.Context
import android.content.SharedPreferences
import android.content.res.Configuration
import android.os.Build
import PreferenceHelper
import PreferenceHelper.get
import PreferenceHelper.set
import java.util.*

object LocaleManagerMew {

    val SELECTED_LANGUAGE = "MEW_CURRENT_-- USER_LANGUAGE"
    var mSharedPreference: SharedPreferences? = null

    var mEnglishFlag = "en"
    var mArabicFlag = "ar"

    fun setLocale(context: Context?): Context {
        return updateResources(context!!, getCurrentLanguage(context)!!)
    }

    inline fun setNewLocale(context: Context, language: String) {

        persistLanguagePreference(context, language)
        updateResources(context, language)
    }

    inline fun getCurrentLanguage(context: Context?): String? {

        var mCurrentLanguage: String?

        if (mSharedPreference == null)
            mSharedPreference = PreferenceHelper.defaultPrefs(context!!)

        mCurrentLanguage = mSharedPreference!![SELECTED_LANGUAGE]

        return mCurrentLanguage
    }

    fun persistLanguagePreference(context: Context, language: String) {
        if (mSharedPreference == null)
            mSharedPreference = PreferenceHelper.defaultPrefs(context)

        mSharedPreference!![SELECTED_LANGUAGE] = language

    }

    fun updateResources(context: Context, language: String): Context {

        var contextFun = context

        var locale = Locale(language)
        Locale.setDefault(locale)

        var resources = context.resources
        var configuration = Configuration(resources.configuration)

        if (Build.VERSION.SDK_INT >= 17) {
            configuration.setLocale(locale)
            contextFun = context.createConfigurationContext(configuration)
        } else {
            configuration.locale = locale
            resources.updateConfiguration(configuration, resources.getDisplayMetrics())
        }
        return contextFun
    }
}
Application Class Code
// ApplicationClass.kt

open class ApplicationClass : Application() {

    init {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(LocaleManager.setLocale(base))
        MultiDex.install(base)
    }

    override fun onCreate() {
        super.onCreate()
      
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        LocaleManagerMew.setLocale(this)
        Log.d(MewConstants.mewLogs, "onConfigurationChanged: " + newConfig.locale.getLanguage())
    }
}

Base Activity
abstract class BaseActivity : AppCompatActivity(){

override fun attachBaseContext(base: Context?) {
    super.attachBaseContext(LocaleManagerMew.setLocale(base))
}
}
XML Layout File Code
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="24dp"
    tools:context=".ui.login.LoginActivity">

    <ImageView
        android:id="@+id/switchLanguage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/eng"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <TextView
        android:id="@+id/txtLoginLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/login"
        android:textAllCaps="true"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/switchLanguage" />


    <ImageView
        android:id="@+id/imgLoginLogo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:src="@drawable/login_logo"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/txtLoginLabel" />

    <TextView
        android:id="@+id/txtCustomer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="6dp"
        android:layout_marginStart="6dp"
        android:layout_marginTop="30dp"
        android:text="@string/customer"
        android:textAllCaps="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imgLoginLogo" />

    <ImageView
        android:id="@+id/imgSwipeLeft"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="@id/txtCustomer"
        app:layout_constraintEnd_toStartOf="@id/txtCustomer"
        app:layout_constraintTop_toTopOf="@+id/txtCustomer"
        app:srcCompat="@drawable/ic_back" />

    <EditText
        android:id="@+id/etCivilID"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:gravity="start"
        android:hint="@string/civil_id"
        android:inputType="number"
        android:maxLength="20"
        android:textAllCaps="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/txtCustomer" />

    <android.support.v7.widget.AppCompatSpinner
        android:id="@+id/spinner_choose_account_login"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:background="@drawable/bg_dropdown"
        android:gravity="start"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etCivilID" />

    <EditText
        android:id="@+id/etPremiseNumber"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:gravity="start"
        android:hint="@string/premise_number"
        android:inputType="number"
        android:maxLength="20"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/spinner_choose_account_login" />

    <TextView
        android:id="@+id/txtForgotPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Forgot password?"
        android:textSize="14sp"
        android:visibility="gone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etPremiseNumber" />

    <Button
        android:id="@+id/btnLogin"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="6dp"
        android:background="@drawable/bg_drawable_orange"
        android:padding="6dp"
        android:text="@string/login"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/btnCreateAccount"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/btnCreateAccount"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="6dp"
        android:background="@drawable/rectangle_orange_boarder_btn_bg"
        android:padding="6dp"
        android:text="@string/create_account"
        android:textColor="@color/colorPrimaryDark"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/btnLogin" />

</android.support.constraint.ConstraintLayout>
Changing Current Language from Activity/Fragment programatically

Here we will first get current language from shared preferences.

If current language saved in shared preferences is arabic we will update language to english.

If current language saved in shared preferences is null, we will assume that user has not updated language dynamically so we will assume current language is english, and we will update current app language to arabic.

Language Change Button Click Listener in our Activity
override fun onClick(p0: View?) {
    when (p0?.id) {
    
        R.id.switchLanguage -> {
            //LocaleManagerMew.setLocale([email protected]?.applicationContext)
            var mCurrentLanguage = LocaleManagerMew.getCurrentLanguage([email protected]?.applicationContext)
            if (mCurrentLanguage == LocaleManagerMew.mArabicFlag) {
                LocaleManagerMew.setNewLocale([email protected]!!, LocaleManagerMew.mEnglishFlag)
            } else if (mCurrentLanguage == LocaleManagerMew.mEnglishFlag) {
                LocaleManagerMew.setNewLocale([email protected]!!, LocaleManagerMew.mArabicFlag)
            }
            activity?.recreate()
        }
    }
}

If you have not missed any part of the code. hopefully everything will be working fine.

If you face any issue you can write in comments sections.

copyright: http://developine.com/

Contact Us