/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.focus.locale.screen import androidx.annotation.VisibleForTesting import mozilla.components.support.base.log.logger.Logger import org.mozilla.focus.locale.Locales import java.text.Collator import java.util.Locale /** * Represents a locale and its native name. */ class LocaleDescriptor(private val localeTag: String) : Comparable { private val languageCodeAndNameMap: HashMap = HashMap() private var nativeName: String? = null init { fillLanguageCodeAndNameMap() setupLocaleDescriptor() } private fun fillLanguageCodeAndNameMap() { // Only ICU 57 actually contains the Asturian name for Asturian, even Android 7.1 is still // shipping with ICU 56, so we need to override the Asturian name (otherwise displayName will // be the current locales version of Asturian, see: // https://github.com/mozilla-mobile/focus-android/issues/634#issuecomment-303886118 languageCodeAndNameMap["ast"] = "Asturianu" // On an Android 8.0 device those languages are not known and we need to add the names // manually. Loading the resources at runtime works without problems though. languageCodeAndNameMap["ace"] = "Acehnese" languageCodeAndNameMap["an"] = "Aragonés" languageCodeAndNameMap["anp"] = "अंगिका" languageCodeAndNameMap["ay"] = "Aimara" languageCodeAndNameMap["bqi"] = "Bakhtiari" languageCodeAndNameMap["cak"] = "Kaqchikel" languageCodeAndNameMap["co"] = "Corsu" languageCodeAndNameMap["hus"] = "Tének" languageCodeAndNameMap["ia"] = "Interlingua" languageCodeAndNameMap["ixl"] = "Ixil" languageCodeAndNameMap["jv"] = "Basa Jawa" languageCodeAndNameMap["meh"] = "Tu'un Savi Yucuhiti" languageCodeAndNameMap["mix"] = "Tu'un savi" languageCodeAndNameMap["nv"] = "Diné bizaad" languageCodeAndNameMap["oc"] = "Occitan" languageCodeAndNameMap["pai"] = "Paa ipai" languageCodeAndNameMap["ppl"] = "Náhuat Pipil" languageCodeAndNameMap["quc"] = "K'iche'" languageCodeAndNameMap["quy"] = "Chanka Qhichwa" languageCodeAndNameMap["skr"] = "سرائیکی" languageCodeAndNameMap["sn"] = "ChiShona" languageCodeAndNameMap["su"] = "Basa Sunda" languageCodeAndNameMap["trs"] = "Triqui" languageCodeAndNameMap["tsz"] = "P'urhepecha" languageCodeAndNameMap["tt"] = "татарча" languageCodeAndNameMap["wo"] = "Wolof" languageCodeAndNameMap["yua"] = "Maayat’aan" languageCodeAndNameMap["zam"] = "DíɁztè" languageCodeAndNameMap["zh-CN"] = "简体中文" languageCodeAndNameMap["zh-TW"] = "正體中文" } private fun setupLocaleDescriptor() { val locale = parseLocaleTag(localeTag) val displayName: String? = getDisplayName(locale) nativeName = when { displayName.isNullOrEmpty() -> { Logger.error("Display name is empty. Using $locale") locale.toString() } Character.getDirectionality(displayName.first()) == Character.DIRECTIONALITY_LEFT_TO_RIGHT -> { // Android OS creates an instance of Transliterator to convert the first letter // of the Greek locale. See CaseMapper.toUpperCase(Locale locale, String s, int count) // Since it's already in upper case, we don't need it displayName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() } } else -> displayName } } @VisibleForTesting internal fun getDisplayName(locale: Locale): String? { return when { languageCodeAndNameMap.containsKey(locale.language) -> { languageCodeAndNameMap[locale.language] } languageCodeAndNameMap.containsKey(locale.toLanguageTag()) -> { languageCodeAndNameMap[locale.toLanguageTag()] } else -> { locale.getDisplayName(locale) } } } private fun parseLocaleTag(localeTag: String): Locale { return Locales.parseLocaleCode(localeTag) } /** * Returns the locale tag (e.g. "en-US"). */ fun getTag(): String { return localeTag } /** * Returns the native name of the locale (e.g. "Deutsch" for "de"). */ fun getNativeName(): String? { return nativeName } override fun hashCode(): Int { return localeTag.hashCode() } override fun equals(other: Any?): Boolean { return other is LocaleDescriptor && compareTo(other) == 0 } override operator fun compareTo(other: LocaleDescriptor): Int { // We sort by name, so we use Collator. return COLLATOR.compare(nativeName, other.nativeName) } companion object { private val COLLATOR = Collator.getInstance(Locale.US) } }