Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eff8beb authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[Locale Preference] Seperate region and numbering system." into udc-dev

parents 5eb022a2 7ba2ef57
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
import android.icu.text.CaseMap;
import android.icu.text.ListFormatter;
import android.icu.text.NumberingSystem;
import android.icu.util.ULocale;
import android.os.LocaleList;
import android.text.TextUtils;
@@ -172,6 +173,21 @@ public class LocaleHelper {
        return lfn.format((Object[]) localeNames);
    }

    /**
     * Returns numbering system value of a locale for display in the provided locale.
     *
     * @param locale The locale whose key value is displayed.
     * @param displayLocale The locale in which to display the key value.
     * @return The string of numbering system.
     */
    public static String getDisplayNumberingSystemKeyValue(
            Locale locale, Locale displayLocale) {
        ULocale uLocale = new ULocale.Builder()
                .setUnicodeLocaleKeyword("nu", NumberingSystem.getInstance(locale).getName())
                .build();
        return uLocale.getDisplayKeywordValue("numbers", ULocale.forLocale(displayLocale));
    }

    /**
     * Adds the likely subtags for a provided locale ID.
     *
+34 −5
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
    private int mTopDistance = 0;
    private CharSequence mTitle = null;
    private OnActionExpandListener mOnActionExpandListener;
    private boolean mIsNumberingSystem = false;

    /**
     * Other classes can register to be notified when a locale was selected.
@@ -90,6 +91,18 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
        boolean hasSpecificPackageName();
    }

    private static LocalePickerWithRegion createNumberingSystemPicker(
            LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
            boolean translatedOnly, OnActionExpandListener onActionExpandListener,
            LocaleCollectorBase localePickerCollector) {
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        localePicker.setOnActionExpandListener(onActionExpandListener);
        localePicker.setIsNumberingSystem(true);
        boolean shouldShowTheList = localePicker.setListener(listener, parent,
                translatedOnly, localePickerCollector);
        return shouldShowTheList ? localePicker : null;
    }

    private static LocalePickerWithRegion createCountryPicker(
            LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
            boolean translatedOnly, OnActionExpandListener onActionExpandListener,
@@ -128,6 +141,10 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
        return localePicker;
    }

    private void setIsNumberingSystem(boolean isNumberingSystem) {
        mIsNumberingSystem = isNumberingSystem;
    }

    /**
     * Sets the listener and initializes the locale list.
     *
@@ -184,6 +201,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
        final boolean hasSpecificPackageName =
                mLocalePickerCollector != null && mLocalePickerCollector.hasSpecificPackageName();
        mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, hasSpecificPackageName);
        mAdapter.setNumberingSystemMode(mIsNumberingSystem);
        final LocaleHelper.LocaleInfoComparator comp =
                new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode);
        mAdapter.sort(comp);
@@ -213,7 +231,6 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
    @Override
    public void onResume() {
        super.onResume();

        if (mParentLocale != null) {
            getActivity().setTitle(mParentLocale.getFullNameNative());
        } else {
@@ -250,16 +267,28 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
        // Special case for resetting the app locale to equal the system locale.
        boolean isSystemLocale = locale.isSystemLocale();
        boolean isRegionLocale = locale.getParent() != null;
        boolean mayHaveDifferentNumberingSystem = locale.hasNumberingSystems();

        if (isSystemLocale || isRegionLocale) {
        if (isSystemLocale
                || (isRegionLocale && !mayHaveDifferentNumberingSystem)
                || mIsNumberingSystem) {
            if (mListener != null) {
                mListener.onLocaleSelected(locale);
            }
            returnToParentFrame();
        } else {
            LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker(
            LocalePickerWithRegion selector;
            if (mayHaveDifferentNumberingSystem) {
                selector =
                        LocalePickerWithRegion.createNumberingSystemPicker(
                        mListener, locale, mTranslatedOnly /* translate only */,
                        mOnActionExpandListener, this.mLocalePickerCollector);
            } else {
                selector = LocalePickerWithRegion.createCountryPicker(
                        mListener, locale, mTranslatedOnly /* translate only */,
                        mOnActionExpandListener, this.mLocalePickerCollector);
            }

            if (selector != null) {
                getFragmentManager().beginTransaction()
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+107 −28
Original line number Diff line number Diff line
@@ -39,6 +39,9 @@ import java.util.Locale;
import java.util.Set;

public class LocaleStore {
    private static final int TIER_LANGUAGE = 1;
    private static final int TIER_REGION = 2;
    private static final int TIER_NUMBERING = 3;
    private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>();
    private static final String TAG = LocaleStore.class.getSimpleName();
    private static boolean sFullyInitialized = false;
@@ -68,10 +71,13 @@ public class LocaleStore {
        private String mFullCountryNameNative;
        private String mLangScriptKey;

        private boolean mHasNumberingSystems;

        private LocaleInfo(Locale locale) {
            this.mLocale = locale;
            this.mId = locale.toLanguageTag();
            this.mParent = getParent(locale);
            this.mHasNumberingSystems = false;
            this.mIsChecked = false;
            this.mSuggestionFlags = SUGGESTION_TYPE_NONE;
            this.mIsTranslated = false;
@@ -93,6 +99,11 @@ public class LocaleStore {
                    .build();
        }

        /** Return true if there are any same locales with different numbering system. */
        public boolean hasNumberingSystems() {
            return mHasNumberingSystems;
        }

        @Override
        public String toString() {
            return mId;
@@ -195,6 +206,10 @@ public class LocaleStore {
            }
        }

        String getNumberingSystem() {
            return LocaleHelper.getDisplayNumberingSystemKeyValue(mLocale, mLocale);
        }

        String getContentDescription(boolean countryMode) {
            if (countryMode) {
                return getFullCountryNameInUiLanguage();
@@ -383,6 +398,12 @@ public class LocaleStore {

        final boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
        Set<Locale> numberSystemLocaleList = new HashSet<>();
        for (String localeId : LocalePicker.getSupportedLocales(context)) {
            if (Locale.forLanguageTag(localeId).getUnicodeLocaleType("nu") != null) {
                numberSystemLocaleList.add(Locale.forLanguageTag(localeId));
            }
        }
        for (String localeId : LocalePicker.getSupportedLocales(context)) {
            if (localeId.isEmpty()) {
                throw new IllformedLocaleException("Bad locale entry in locale_config.xml");
@@ -403,6 +424,12 @@ public class LocaleStore {
            if (simCountries.contains(li.getLocale().getCountry())) {
                li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
            }
            numberSystemLocaleList.forEach(l -> {
                if (li.getLocale().stripExtensions().equals(l.stripExtensions())) {
                    li.mHasNumberingSystems = true;
                }
            });

            sLocaleCache.put(li.getId(), li);
            final Locale parent = li.getParent();
            if (parent != null) {
@@ -445,20 +472,43 @@ public class LocaleStore {
        sFullyInitialized = true;
    }

    private static int getLevel(Set<String> ignorables, LocaleInfo li, boolean translatedOnly) {
        if (ignorables.contains(li.getId())) return 0;
        if (li.mIsPseudo) return 2;
        if (translatedOnly && !li.isTranslated()) return 0;
        if (li.getParent() != null) return 2;
        return 0;
    private static boolean isShallIgnore(
            Set<String> ignorables, LocaleInfo li, boolean translatedOnly) {
        if (ignorables.stream().anyMatch(tag ->
                Locale.forLanguageTag(tag).stripExtensions()
                        .equals(li.getLocale().stripExtensions()))) {
            return true;
        }
        if (li.mIsPseudo) return false;
        if (translatedOnly && !li.isTranslated()) return true;
        if (li.getParent() != null) return false;
        return true;
    }

    private static int getLocaleTier(LocaleInfo parent) {
        if (parent == null) {
            return TIER_LANGUAGE;
        } else if (parent.getLocale().getCountry().isEmpty()) {
            return TIER_REGION;
        } else {
            return TIER_NUMBERING;
        }
    }

    /**
     * Returns a list of locales for language or region selection.
     *
     * If the parent is null, then it is the language list.
     *
     * If it is not null, then the list will contain all the locales that belong to that parent.
     * Example: if the parent is "ar", then the region list will contain all Arabic locales.
     * (this is not language based, but language-script, so that it works for zh-Hant and so on.
     * (this is not language based, but language-script, so that it works for zh-Hant and so on.)
     *
     * If it is not null and has country, then the list will contain all locales with that parent's
     * language and country, i.e. containing alternate numbering systems.
     *
     * Example: if the parent is "ff-Adlm-BF", then the numbering list will contain all
     * Fula (Adlam, Burkina Faso) i.e. "ff-Adlm-BF" and "ff-Adlm-BF-u-nu-latn"
     */
    @UnsupportedAppUsage
    public static Set<LocaleInfo> getLevelLocales(Context context, Set<String> ignorables,
@@ -478,28 +528,49 @@ public class LocaleStore {
     */
    public static Set<LocaleInfo> getLevelLocales(Context context, Set<String> ignorables,
            LocaleInfo parent, boolean translatedOnly, LocaleList explicitLocales) {
        if (context != null) {
            fillCache(context);
        String parentId = parent == null ? null : parent.getId();
        HashSet<LocaleInfo> result = new HashSet<>();
        }
        HashMap<String, LocaleInfo> supportedLcoaleInfos =
                explicitLocales == null
                        ? sLocaleCache
                        : convertExplicitLocales(explicitLocales, sLocaleCache.values());
        return getTierLocales(ignorables, parent, translatedOnly, supportedLcoaleInfos);
    }

    private static Set<LocaleInfo> getTierLocales(
            Set<String> ignorables,
            LocaleInfo parent,
            boolean translatedOnly,
            HashMap<String, LocaleInfo> supportedLcoaleInfos) {

        boolean hasTargetParent = parent != null;
        String parentId = hasTargetParent ? parent.getId() : null;
        HashSet<LocaleInfo> result = new HashSet<>();
        for (LocaleStore.LocaleInfo li : supportedLcoaleInfos.values()) {
            int level = getLevel(ignorables, li, translatedOnly);
            if (level == 2) {
                if (parent != null) { // region selection
                    if (parentId.equals(li.getParent().toLanguageTag())) {
                        result.add(li);
            if (isShallIgnore(ignorables, li, translatedOnly)) {
                continue;
            }
                } else { // language selection
            switch(getLocaleTier(parent)) {
                case TIER_LANGUAGE:
                    if (li.isSuggestionOfType(LocaleInfo.SUGGESTION_TYPE_SIM)) {
                        result.add(li);
                    } else {
                        result.add(getLocaleInfo(li.getParent()));
                        result.add(getLocaleInfo(li.getParent(), supportedLcoaleInfos));
                    }
                    break;
                case TIER_REGION:
                    if (parentId.equals(li.getParent().toLanguageTag())) {
                        result.add(getLocaleInfo(
                                li.getLocale().stripExtensions(), supportedLcoaleInfos));
                    }
                    break;
                case TIER_NUMBERING:
                    if (parent.getLocale().stripExtensions()
                            .equals(li.getLocale().stripExtensions())) {
                        result.add(li);
                    }
                    break;
            }
        }
        return result;
@@ -538,18 +609,21 @@ public class LocaleStore {
    }

    private static LocaleList matchLocaleFromSupportedLocaleList(
            LocaleList explicitLocales, Collection<LocaleInfo> localeinfo) {
            LocaleList explicitLocales, Collection<LocaleInfo> localeInfos) {
        if (localeInfos == null) {
            return explicitLocales;
        }
        //TODO: Adds a function for unicode extension if needed.
        Locale[] resultLocales = new Locale[explicitLocales.size()];
        for (int i = 0; i < explicitLocales.size(); i++) {
            Locale locale = explicitLocales.get(i).stripExtensions();
            Locale locale = explicitLocales.get(i);
            if (!TextUtils.isEmpty(locale.getCountry())) {
                for (LocaleInfo localeInfo :localeinfo) {
                for (LocaleInfo localeInfo :localeInfos) {
                    if (LocaleList.matchesLanguageAndScript(locale, localeInfo.getLocale())
                            && TextUtils.equals(locale.getCountry(),
                            localeInfo.getLocale().getCountry())) {
                        resultLocales[i] = localeInfo.getLocale();
                        continue;
                        break;
                    }
                }
            }
@@ -562,18 +636,23 @@ public class LocaleStore {

    @UnsupportedAppUsage
    public static LocaleInfo getLocaleInfo(Locale locale) {
        return getLocaleInfo(locale, sLocaleCache);
    }

    private static LocaleInfo getLocaleInfo(
            Locale locale, HashMap<String, LocaleInfo> localeInfos) {
        String id = locale.toLanguageTag();
        LocaleInfo result;
        if (!sLocaleCache.containsKey(id)) {
        if (!localeInfos.containsKey(id)) {
            // Locale preferences can modify the language tag to current system languages, so we
            // need to check the input locale without extra u extension except numbering system.
            Locale filteredLocale = new Locale.Builder()
                    .setLocale(locale.stripExtensions())
                    .setUnicodeLocaleKeyword("nu", locale.getUnicodeLocaleType("nu"))
                    .build();
            if (sLocaleCache.containsKey(filteredLocale.toLanguageTag())) {
            if (localeInfos.containsKey(filteredLocale.toLanguageTag())) {
                result = new LocaleInfo(locale);
                LocaleInfo localeInfo = sLocaleCache.get(filteredLocale.toLanguageTag());
                LocaleInfo localeInfo = localeInfos.get(filteredLocale.toLanguageTag());
                // This locale is included in supported locales, so follow the settings
                // of supported locales.
                result.mIsPseudo = localeInfo.mIsPseudo;
@@ -582,9 +661,9 @@ public class LocaleStore {
                return result;
            }
            result = new LocaleInfo(locale);
            sLocaleCache.put(id, result);
            localeInfos.put(id, result);
        } else {
            result = sLocaleCache.get(id);
            result = localeInfos.get(id);
        }
        return result;
    }
+15 −5
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
    protected ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions;
    protected int mSuggestionCount;
    protected final boolean mCountryMode;
    protected boolean mIsNumberingMode;
    protected LayoutInflater mInflater;

    protected Locale mDisplayLocale = null;
@@ -89,6 +90,14 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
        }
    }

    public void setNumberingSystemMode(boolean isNumberSystemMode) {
        mIsNumberingMode = isNumberSystemMode;
    }

    public boolean getIsForNumberingSystem() {
        return mIsNumberingMode;
    }

    @Override
    public boolean areAllItemsEnabled() {
        return false;
@@ -209,7 +218,6 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
        if (convertView == null && mInflater == null) {
            mInflater = LayoutInflater.from(parent.getContext());
        }

        int itemType = getItemViewType(position);
        View itemView = getNewViewIfNeeded(convertView, parent, itemType, position);
        switch (itemType) {
@@ -217,13 +225,13 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
            case TYPE_HEADER_ALL_OTHERS:
                TextView textView = (TextView) itemView;
                if (itemType == TYPE_HEADER_SUGGESTED) {
                   if (mCountryMode) {
                    if (mCountryMode && !mIsNumberingMode) {
                        setTextTo(textView, R.string.language_picker_regions_section_suggested);
                    } else {
                        setTextTo(textView, R.string.language_picker_section_suggested);
                    }
                } else {
                    if (mCountryMode) {
                    if (mCountryMode && !mIsNumberingMode) {
                        setTextTo(textView, R.string.region_picker_section_all);
                    } else {
                        setTextTo(textView, R.string.language_picker_section_all);
@@ -419,9 +427,11 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {

    private void updateTextView(View convertView, TextView text, int position) {
        LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position);
        text.setText(item.getLabel(mCountryMode));
        text.setText(mIsNumberingMode
                ? item.getNumberingSystem() : item.getLabel(mCountryMode));
        text.setTextLocale(item.getLocale());
        text.setContentDescription(item.getContentDescription(mCountryMode));
        text.setContentDescription(mIsNumberingMode
                        ? item.getNumberingSystem() : item.getContentDescription(mCountryMode));
        if (mCountryMode) {
            int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent());
            //noinspection ResourceType
+107 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IllformedLocaleException;
import java.util.List;
import java.util.Locale;
@@ -141,14 +142,118 @@ public class LocaleStoreTest {

        HashMap<String, LocaleInfo> result =
                LocaleStore.convertExplicitLocales(locales, supportedLocale);

        assertEquals("en", result.get("en").getId());
        assertEquals("en-US", result.get("en-US").getId());
        assertNull(result.get("en-Latn-US"));
    }

    @Test
    public void getLevelLocales_languageTier_returnAllSupportLanguages() {
        LocaleList testSupportedLocales =
                LocaleList.forLanguageTags(
                        "en-US,zh-Hant-TW,ja-JP,en-GB,bn-IN-u-nu-arab,ks-Arab-IN,bn-IN");

        Set<String> ignorableLocales = new HashSet<>();
        ignorableLocales.add("zh-Hant-HK");
        LocaleInfo parent = null;

        Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
                null, ignorableLocales, parent, false, testSupportedLocales);

        assertEquals(5, localeInfos.size());
        localeInfos.forEach(localeInfo -> {
            assertTrue(localeInfo.getLocale().getCountry().isEmpty());
        });
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("en")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("zh-Hant")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("ja")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("bn")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("ks-Arab")));
    }

    @Test
    public void getLevelLocales_regionTierAndParentIsEn_returnEnLocales() {
        LocaleList testSupportedLocales =
                LocaleList.forLanguageTags(
                        "en-US,en-GB,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN");
        Set<String> ignorableLocales = new HashSet<>();
        ignorableLocales.add("zh-Hant-HK");
        LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("en"));

        Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
                null, ignorableLocales, parent, false, testSupportedLocales);

        assertEquals(3, localeInfos.size());
        localeInfos.forEach(localeInfo -> {
            assertEquals("en", localeInfo.getLocale().getLanguage());
        });
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("en-US")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("en-GB")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("en-ZA")));
    }

    @Test
    public void getLevelLocales_numberingTierAndParentIsBnIn_returnBnInLocales() {
        LocaleList testSupportedLocales =
                LocaleList.forLanguageTags(
                        "en-US,zh-Hant-TW,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN,bn-IN-u-nu-adlm");
        Set<String> ignorableLocales = new HashSet<>();
        ignorableLocales.add("zh-Hant-HK");
        LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("bn"));

        Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
                null, ignorableLocales, parent, false, testSupportedLocales);

        assertEquals(1, localeInfos.size());
        assertEquals("bn-IN", localeInfos.iterator().next().getLocale().toLanguageTag());
    }

    @Test
    public void getLevelLocales_regionTierAndParentIsBnInAndIgnoreBn_returnEmpty() {
        LocaleList testSupportedLocales =
                LocaleList.forLanguageTags(
                        "en-US,zh-Hant-TW,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN,bn-IN-u-nu-adlm");
        Set<String> ignorableLocales = new HashSet<>();
        ignorableLocales.add("bn-IN");
        LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("bn-IN"));

        Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
                null, ignorableLocales, parent, false, testSupportedLocales);

        assertEquals(0, localeInfos.size());
    }

    @Test
    public void getLevelLocales_regionTierAndParentIsBnIn_returnBnLocaleFamily() {
        LocaleList testSupportedLocales =
                LocaleList.forLanguageTags(
                        "en-US,zh-Hant-TW,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN,bn-IN-u-nu-adlm");
        Set<String> ignorableLocales = new HashSet<>();
        ignorableLocales.add("en-US");
        LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("bn-IN"));

        Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
                null, ignorableLocales, parent, false, testSupportedLocales);

        assertEquals(3, localeInfos.size());
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("bn-IN-u-nu-adlm")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("bn-IN-u-nu-arab")));
        assertTrue(localeInfos.stream().anyMatch(
                info -> info.getLocale().toLanguageTag().equals("bn-IN")));
    }

    private ArrayList<LocaleInfo> getFakeSupportedLocales() {
        String[] locales = {"en-US", "zh-Hant-TW", "ja-JP", "en-GB"};
        String[] locales = {"en-US", "zh-Hant-TW", "ja-JP", "en-GB", "en-US-u-nu-arab"};
        ArrayList<LocaleInfo> supportedLocales = new ArrayList<>();
        for (String localeTag : locales) {
            supportedLocales.add(LocaleStore.fromLocale(Locale.forLanguageTag(localeTag)));