Get list of locales from locale-config in Android 13
Asked Answered
S

1

11

To ensure your app's languages are configurable in system settings on devices running Android 13 or higher, we need to create a locales_config XML file and add it our app's manifest using the android:localeConfig attribute (see here).

For example, locales_config.xml might contain:

<?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="en-GB"/>
   <locale android:name="fr"/>
   <locale android:name="ja"/>
   <locale android:name="zh-Hans-MO"/>
   <locale android:name="zh-Hant-MO"/>
</locale-config>

If we also want to provide a custom locale picker in our app's settings, how do we retrieve the list of supported locales from locales_config.xml in order to populate our picker (without duplicating the list in the locale picker code)?

Statistical answered 18/10, 2022 at 16:4 Comment(3)
Given a Context, call getResources().getXml(R.xml.locales_config) to get an XmlResourceParser on the contents of your XML resource.Ottinger
@Ottinger seems quite low level... which is fine (and it would work) but I would have hoped that the new Android 13 locale system would provide a dedicated method for querying this information so that we can implement our own in-app locale picker to mirror what is available on an Android 13 device (but not earlier devices)?Statistical
"I would have hoped that the new Android 13 locale system would provide a dedicated method for querying this information" -- I cannot rule that out. I do not remember an option for this, but I haven't spent a lot of time with this particular aspect of Android 13.Ottinger
S
6

Here's the kotlin util class I use for this purpose:

LanguageUtil class


fun Context.getLocaleListFromXml(): LocaleListCompat {
    val tagsList = mutableListOf<CharSequence>()
    try {
        val xpp: XmlPullParser = resources.getXml(R.xml.locales_config)
        while (xpp.eventType != XmlPullParser.END_DOCUMENT) {
            if (xpp.eventType == XmlPullParser.START_TAG) {
                if (xpp.name == "locale") {
                    tagsList.add(xpp.getAttributeValue(0))
                }
            }
            xpp.next()
        }
    } catch (e: XmlPullParserException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }

    return LocaleListCompat.forLanguageTags(tagsList.joinToString(","))
}


fun Context.getLangPreferenceDropdownEntries(): Map<String, String> {

    val localeList = getLocaleListFromXml()
    val map = mutableMapOf<String, String>()

    for (a in 0 until localeList.size()) {
        localeList[a].let {
            map.put(it.getDisplayName(it), it.toLanguageTag())
        }
    }
    return map
}


fun setLocale(langTag: String){
  val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(langTag)
   AppCompatDelegate.setApplicationLocales(appLocale)
}

Example usage:

 val map = requireContext().getLangPreferenceDropdownEntries()

 findPreference<DropDownPreference>("key_lang")?.apply {
        entries = map.keys.toTypedArray()
        entryValues = map.values.toTypedArray()
        onPreferenceChangeListener =  Preference.OnPreferenceChangeListener { _, newValue ->
              (newValue as String).let{
                     value = it
                     setLocale(it)
              }
              true
           }
     }

Requires Appcompat 1.6.0-beta01 or higher

Slavonic answered 7/12, 2022 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.