Hello everyone,
Today in this article, we are going to learn about localisation to support the multiple languages in our android apps. Localisation is the basic need for the app as we want to engage the app for the different regions to make them understand their language. Generally, we handle the localisation in two different approaches.
Let’s take an example of two languages English
and Hindi
to support our app. So first of all we need to create the two folders in the res/values.
Now we need to add our sting.xml file in each folder respectively.
<resources> <string name="title">Hello, I am the life saver.</string> <string name="select_language">Select Language</string> </resources>
<resources> <string name="title">नमस्ते, मैं जीवन रक्षक हूँ।</string> <string name="select_language">भाषा चुने</string> </resources>
Now let’s say we provide ab option to the user to select the language based on the there choice and comfort.
object LanguageList { val languageList: ArrayList<LanguageDTO> get() { val list: MutableList<LanguageDTO> = ArrayList<LanguageDTO>() list.add(LanguageDTO("en", "English")) list.add(LanguageDTO("hi", "हिंदी")) return list.toList() as ArrayList<LanguageDTO> } } @Parcelize data class LanguageDTO(val languageCode: String, val languageTitle: String): Parcelable
Let’s create an LocaleManager
file to update the resource configuration when user select any language and save into memory to support persistence of the this language.
object LocaleManager { fun setNewLocale(c: Context?, language: String) { updateResources(c, language) } private fun updateResources(context: Context?, language: String) { val locale = Locale(language) Locale.setDefault(locale) if (context != null) { val res = context.resources val config = Configuration(res.configuration) config.locale = locale res.updateConfiguration(config, res.displayMetrics) } } }
Now let’s create a SharedPref file to save the selected language code to persist.
object SharedPref { private const val PREFS_NAME = "Language_prefs" private var sharedPreferences: SharedPreferences? = null private fun getInstance(context: Context): SharedPreferences? { if (sharedPreferences == null) sharedPreferences = context.getSharedPreferences( PREFS_NAME, Context.MODE_PRIVATE ) return sharedPreferences } fun getStringParams(aContext: Context, paramName: String?, defaultValue: String?): String? { return getInstance(aContext)?.getString(paramName, defaultValue) } @SuppressLint("ApplySharedPref") fun setStringParams(aContext: Context, paramName: String?, paramValue: String?) { val editor = getInstance(aContext)?.edit() editor?.putString(paramName, paramValue) editor?.commit() } }
object SharedPrefsConstants { const val USER_SELECTED_LANGUAGE_CODE = "selectedLanguageCode" }
Now whenever we load the sting id from the resource, first we need to get the language code from the saved preference update the locale to get the correct string id from the resources. Let’s create the common function to call for those string which required for localise.
object LocalizationUtil { fun getLocalisedString(context: Context, strId: Int): String { val lan = SharedPref.getStringParams( context, SharedPrefsConstants.USER_SELECTED_LANGUAGE_CODE, null ) if (lan != null) LocaleManager.setNewLocale(context, lan) return context.resources.getString(strId) } }
So for here we are done for update the language and save the selected language into the SharedPref. Now we need to create the view to show and select option provide to user to change the language. So let create an fragment xml file.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:padding="16dp"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/tvTitle" android:layout_marginTop="200dp" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:text="@string/title"/> <Button android:id="@+id/selectLanguage" android:layout_width="match_parent" android:layout_marginTop="20dp" android:layout_height="wrap_content" android:text="@string/select_language" app:layout_constraintTop_toBottomOf="@+id/tvTitle"/> </androidx.constraintlayout.widget.ConstraintLayout>
tvTitle.text = LocalizationUtil.getLocalisedString(requireContext(), R.string.title) selectLanguage.text = LocalizationUtil.getLocalisedString(requireContext(), R.string.select_language)
we can load the restring file by calling this function and when user change the language from the option then we need to start the activity.
it?.let { clickLanguage -> SharedPref.setStringParams( requireContext(), SharedPrefsConstants.USER_SELECTED_LANGUAGE_CODE, clickLanguage.languageCode ) requireActivity().finish() requireActivity().startActivity(requireActivity().intent) }
This is all the for localisation change and update the configuration. Let me add the full code here.
class FirstFragment : Fragment(R.layout.first_fragment) { private var binding: FirstFragmentBinding? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FirstFragmentBinding.bind(view) initView() } private fun initView() { binding?.apply { tvTitle.text = LocalizationUtil.getLocalisedString(requireContext(), R.string.title) selectLanguage.text = LocalizationUtil.getLocalisedString(requireContext(), R.string.select_language) selectLanguage.setOnClickListener { val languageCode: String? = SharedPref.getStringParams( requireContext(), SharedPrefsConstants.USER_SELECTED_LANGUAGE_CODE, null ) showBottomSheet(LanguageList.languageList, languageCode) } } } override fun onDestroyView() { super.onDestroyView() binding = null } private fun showBottomSheet(list: ArrayList<LanguageDTO>, selectedItemId: String?) { val bottomSheetFragment = LanguageChangeBottomSheet { it?.let { clickLanguage -> SharedPref.setStringParams( requireContext(), SharedPrefsConstants.USER_SELECTED_LANGUAGE_CODE, clickLanguage.languageCode ) requireActivity().finish() requireActivity().startActivity(requireActivity().intent) } } val args = Bundle() args.putParcelableArrayList(LanguageChangeBottomSheet.LANGUAGE_LIST, list) args.putString(LanguageChangeBottomSheet.SELECTED_LANGUAGE_COED, selectedItemId) bottomSheetFragment.arguments = args bottomSheetFragment.show( requireActivity().supportFragmentManager, bottomSheetFragment.tag ) } }
class LanguageChangeBottomSheet( private val listener: (LanguageDTO?) -> Unit ) : BottomSheetDialogFragment() { private var binding: LanguageChangeSheetBinding? = null private var genericList: ArrayList<LanguageDTO> = arrayListOf<LanguageDTO>() private var adapter: LanguageAdapter? = null private var selectedLanguageCode: String? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { dialog?.setOnShowListener { dialog -> val d: BottomSheetDialog = dialog as BottomSheetDialog val bottomSheetInternal: View = d.findViewById(com.google.android.material.R.id.design_bottom_sheet)!! val bottomBehavior = BottomSheetBehavior.from(bottomSheetInternal) bottomBehavior.state = BottomSheetBehavior.STATE_EXPANDED bottomBehavior.isDraggable = false } return inflater.inflate(R.layout.language_change_sheet, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) genericList = arguments?.getParcelableArrayList<LanguageDTO>(LANGUAGE_LIST) as ArrayList<LanguageDTO> selectedLanguageCode = arguments?.getString(SELECTED_LANGUAGE_COED) ?: "en" binding = LanguageChangeSheetBinding.bind(view) initView() } private fun initView() { binding?.apply { // setup recyclerview recyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) val itemDecoration = DividerItemDecoration(context, DividerItemDecoration.VERTICAL) ContextCompat.getDrawable(requireContext(), R.drawable.layer_divider) ?.let { itemDecoration.setDrawable(it) } recyclerView.addItemDecoration(itemDecoration) setRecyclerViewFixedHeight() attachAdapter(genericList) } } private fun LanguageChangeSheetBinding.setRecyclerViewFixedHeight() { val displayMetrics = DisplayMetrics() requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) val height = displayMetrics.heightPixels * 70 / 100 recyclerView.layoutParams.height = height } private fun attachAdapter(list: ArrayList<LanguageDTO>) { binding?.apply { adapter = LanguageAdapter(list, itemOnClick, selectedLanguageCode) recyclerView.adapter = adapter } } @SuppressLint("NotifyDataSetChanged") val itemOnClick: (LanguageDTO) -> Unit = { languageDTO -> lifecycleScope.launch { delay(100) adapter?.notifyDataSetChanged() delay(100) listener.invoke(languageDTO) dismiss() } } companion object { const val LANGUAGE_LIST = "language_list" const val SELECTED_LANGUAGE_COED = "selected_language_code" } }
class LanguageAdapter( private var addressList: ArrayList<LanguageDTO>, private val itemClickListener: (LanguageDTO) -> Unit, private var selectedItemId: String? ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { var languageFilterList = ArrayList<LanguageDTO>() class LanguageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) init { languageFilterList = addressList } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val listView = LayoutInflater.from(parent.context).inflate(R.layout.language_item, parent, false) return LanguageHolder(listView) } override fun getItemCount(): Int { return languageFilterList.size } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val language = languageFilterList[position] holder.itemView.tv_title.text = language.languageTitle if (language.languageCode == selectedItemId) { holder.itemView.radioButton.setImageDrawable( ContextCompat.getDrawable( holder.itemView.context, R.drawable.ic_language_selected ) ) } else { holder.itemView.radioButton.setImageDrawable( ContextCompat.getDrawable( holder.itemView.context, R.drawable.ic_language_unselected ) ) } holder.itemView.setOnClickListener { holder.itemView.radioButton.setImageDrawable( ContextCompat.getDrawable( holder.itemView.context, R.drawable.ic_language_selected ) ) selectedItemId = language.languageCode itemClickListener(language) } } }
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com
Hi everyone, In this article, we are going to learn how to hide the production… Read More
Hello everyone, In this article, we are going to learn something to handle the callback… Read More
In this article, we are learning about the run time permissions for request permission launchers.… Read More
Hello everyone. In my last tutorial, we learned about the Jetpack Compose introduction and about applying the… Read More
Hello everyone, In this article, we are going to learn about the Jetpack Compose with… Read More
Hello everyone, In this article, we are going to learn how to use layouts, rows,… Read More