Skip to content

Instantly share code, notes, and snippets.

@Doc1faux
Created February 22, 2022 09:36
Show Gist options
  • Save Doc1faux/410a80db6fc17d45b4e109b4849424c9 to your computer and use it in GitHub Desktop.
Save Doc1faux/410a80db6fc17d45b4e109b4849424c9 to your computer and use it in GitHub Desktop.
A one-shot function to get a default country for a locale. I was needed this method in order to display a country flag from a language code only (ISO 639-2 & 639-3)
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public final class LocaleUtils
{
private static Map<String, String> sCountryForLocale;
/**
* @return a Map listing for each locale (ISO 639-2 & 639-3)
* the corresponding default country (ISO 3166-1)
*/
@NonNull
public static final Map<String, String> countryForLocale()
{
if (sCountryForLocale == null)
{
sCountryForLocale = new HashMap<String, String>() {{
// Add country for locale which could not be determined by logic
// i.e. more than 1 possible country and country code != locale code.
// Based on ISO 639-2 & 639-3 wikipedia articles:
// https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
// https://en.wikipedia.org/wiki/Wikipedia:WikiProject_Languages/List_of_ISO_639-3_language_codes_(2019)
// Selection has been done in this order:
// - Official language in only 1 country
// - Official language in more than 1 country but spoken widely only in 1 (e.g. Bengali)
// - Official language in more than 1 country ordered by speakers (e.g. Hausa)
// - Recognised regional/minority language in only 1 country
// - Recognised regional/minority language in more than 1 country ordered by speakers (e.g. Low German)
// - Native country ordered by population (e.g. Alemannic)
// - Flag likeness (e.g. Arabic spoken by countries of the Arab League)
put("af", "za"); put("ar", "sa"); put("bn", "bd"); put("bo", "cn"); put("ca", "ad");
put("ccp", "bd"); put("ckb", "iq"); put("da", "dk"); put("ee", "gh"); put("el", "gr");
put("en", "gb"); put("fa", "ir"); put("ff", "cm"); put("gsw", "ch"); put("ha", "ng");
put("ko", "kr"); put("ln", "cd"); put("lrc", "ir"); put("mas", "ke"); put("ms", "my");
put("nb", "no"); put("nds", "de"); put("ne", "np"); put("nmg", "cm"); put("om", "et");
put("os", "ru"); put("pa", "in"); put("qu", "bo"); put("se", "no"); put("sq", "al");
put("sr", "rs"); put("sv", "se"); put("sw", "tz"); put("ta", "lk"); put("teo", "ug");
put("ti", "er"); put("ur", "pk"); put("ji", "ru"); put("yo", "ng"); put("yue", "hk");
put("zh", "cn");
}};
// For others, do it by logic ;)
Locale[] availableLocales = Locale.getAvailableLocales();
String previousLanguage = availableLocales[0].getLanguage();
List<String> previousCountries = new ArrayList<>();
// Firstly, we create a clean map of countries by locale
HashMap<String, List<String>> countriesForLocales = new HashMap<>(availableLocales.length);
for (Locale locale : availableLocales)
{
String language = locale.getLanguage();
// We ignore already added locales
if (sCountryForLocale.containsKey(language))
{
continue;
}
String country = locale.getCountry();
if (! language.equals(previousLanguage))
{
// This happens for Esperanto
if (! previousCountries.isEmpty())
{
countriesForLocales.put(previousLanguage, previousCountries);
}
previousLanguage = language;
previousCountries = country.isEmpty() ? new ArrayList<>()
: new ArrayList<>(Collections.singleton(country));
}
// This happens for base locale (without specified country)
// or locale with several alphabets (e.g. Serbian)
else if (! country.isEmpty() && ! previousCountries.contains(country))
{
previousCountries.add(country);
}
}
if (! sCountryForLocale.containsKey(previousLanguage)
&& ! countriesForLocales.containsKey(previousLanguage)
&& ! previousCountries.isEmpty())
{
countriesForLocales.put(previousLanguage, previousCountries);
}
// Now we populate our prefilled map with our locale-country tuples
for (Map.Entry<String, List<String>> countriesForLocale : countriesForLocales.entrySet())
{
String locale = countriesForLocale.getKey();
List<String> countries = countriesForLocale.getValue();
// If only one, we take it
if (countries.size() == 1)
{
sCountryForLocale.put(locale, countries.get(0).toLowerCase());
}
// Else we search for country code == locale code
else
{
int index = countries.indexOf(locale.toUpperCase());
if (index > -1)
{
sCountryForLocale.put(locale, countries.get(index).toLowerCase());
}
}
}
}
return sCountryForLocale;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment