/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ide.common.resources;

import static java.util.Locale.US;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.ide.common.res2.ResourceRepository;
import com.android.resources.ResourceType;
import com.android.utils.SdkUtils;
import com.google.common.base.Splitter;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;

/**
 * The {@linkplain LocaleManager} provides access to locale information such as
 * language names and language to region name mappings for the various locales.
 */
public class LocaleManager {
    /** Utility methods only */
    private LocaleManager() {
    }

    /**
     * Returns the name of the given region for a 2 letter region code, in English.
     *
     * @param regionCode the 2 letter region code (ISO 3166-1 alpha-2),
     *                   or the 3 letter region ode (ISO 3166-2 alpha-3)
     * @return the name of the given region for a region code, in English, or
     *         null if not known
     */
    @Nullable
    public static String getRegionName(@NonNull String regionCode) {
        if (regionCode.length() == 2) {
            assert Character.isUpperCase(regionCode.charAt(0))
                    && Character.isUpperCase(regionCode.charAt(1)) : regionCode;
            int index = Arrays.binarySearch(ISO_3166_1_CODES, regionCode);
            if (index < 0 || index >= ISO_3166_1_TO_2.length) {
                return null;
            }
            return ISO_3166_2_NAMES[ISO_3166_1_TO_2[index]];
        } else if (regionCode.length() == 3) {
            assert Character.isUpperCase(regionCode.charAt(0))
                    && Character.isUpperCase(regionCode.charAt(1))
                    && Character.isUpperCase(regionCode.charAt(2)) : regionCode;
            int index = Arrays.binarySearch(ISO_3166_2_CODES, regionCode);
            if (index < 0) {
                return null;
            }
            return ISO_3166_2_NAMES[index];
        }

        return null;
    }

    /**
     * Returns the name of the given language for a language code, in English.
     *
     * @param languageCode the 2 letter language code (ISO 639-1), or
     *                     3 letter language code (ISO 639-2)
     * @return the name of the given language for a language code, in English, or
     *         null if not known
     */
    @Nullable
    public static String getLanguageName(@NonNull String languageCode) {
        if (languageCode.length() == 2) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1)) : languageCode;
            int index = Arrays.binarySearch(ISO_639_1_CODES, languageCode);
            if (index < 0 || index >= ISO_639_1_TO_2.length) {
                return null;
            }
            return ISO_639_2_NAMES[ISO_639_1_TO_2[index]];
        } else if (languageCode.length() == 3) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1))
                    && Character.isLowerCase(languageCode.charAt(2)) : languageCode;
            int index = Arrays.binarySearch(ISO_639_2_CODES, languageCode);
            if (index < 0) {
                return null;
            }
            return ISO_639_2_NAMES[index];
        }

        return null;
    }

    /**
     * Returns all the known language codes
     *
     * @return all the known language codes
     */
    @NonNull
    public static List<String> getLanguageCodes() {
        return getLanguageCodes(false);
    }

    /**
     * Returns all the known language codes
     *
     * @param include3 If true, include 3-letter language codes as well (for
     *                 languages not available as 2-letter languages)
     * @return all the known language codes
     */
    @NonNull
    public static List<String> getLanguageCodes(boolean include3) {
        if (!include3) {
            return Arrays.asList(ISO_639_1_CODES);
        } else {
            List<String> codes = Lists.newArrayListWithExpectedSize(ISO_639_2_CODES.length);
            for (int i = 0; i < ISO_639_2_TO_1.length; i++) {
                int iso2 = ISO_639_2_TO_1[i];
                if (iso2 != -1) {
                    codes.add(ISO_639_1_CODES[iso2]);
                } else {
                    codes.add(ISO_639_2_CODES[i]);
                }
            }
            return codes;
        }
    }

    /**
     * Returns all the known region codes
     *
     * @return all the known region codes
     */
    @NonNull
    public static List<String> getRegionCodes() {
        return getRegionCodes(false);
    }

    /**
     * Returns all the known region codes
     *
     * @param include3 If true, include 3-letter region codes as well (for
     *                 regions not available as 2-letter regions)
     * @return all the known region codes
     */
    @NonNull
    public static List<String> getRegionCodes(boolean include3) {
        if (!include3) {
            return Arrays.asList(ISO_3166_1_CODES);
        } else {
            List<String> codes = Lists.newArrayListWithExpectedSize(ISO_3166_2_CODES.length);
            for (int i = 0; i < ISO_3166_2_TO_1.length; i++) {
                int iso2 = ISO_3166_2_TO_1[i];
                if (iso2 != -1) {
                    codes.add(ISO_3166_1_CODES[iso2]);
                } else {
                    codes.add(ISO_3166_2_CODES[i]);
                }
            }
            return codes;
        }
    }

    /**
     * Returns true if the given language code represents a valid/known 2 or 3 letter
     * language code. (By convention, language codes should be lower case.)
     *
     * @param languageCode the language code to look up
     * @return true if this is a known language
     */
    public static boolean isValidLanguageCode(@NonNull String languageCode) {
        if (languageCode.length() == 2) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1)) : languageCode;
            return Arrays.binarySearch(ISO_639_1_CODES, languageCode) >= 0;
        } else if (languageCode.length() == 3) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1))
                    && Character.isLowerCase(languageCode.charAt(2)) : languageCode;
            return Arrays.binarySearch(ISO_639_2_CODES, languageCode) >= 0;
        }

        return false;
    }

    /**
     * Returns true if the given region code represents a valid/known 2 or 3 letter
     * region code. (By convention, region codes should be upper case.)
     *
     * @param regionCode the region code to look up
     * @return true if this is a known region
     */
    public static boolean isValidRegionCode(@NonNull String regionCode) {
        if (regionCode.length() == 2) {
            assert Character.isUpperCase(regionCode.charAt(0))
                    && Character.isUpperCase(regionCode.charAt(1)) : regionCode;
            return Arrays.binarySearch(ISO_3166_1_CODES, regionCode) >= 0;
        } else if (regionCode.length() == 3) {
            assert Character.isUpperCase(regionCode.charAt(0))
                    && Character.isUpperCase(regionCode.charAt(1))
                    && Character.isUpperCase(regionCode.charAt(2)) : regionCode;
            return Arrays.binarySearch(ISO_3166_2_CODES, regionCode) >= 0;
        }

        return false;
    }

    /**
     * Returns the region code for the given language. <b>Note that there can be
     * many regions that speak a given language; this just picks one</b> based
     * on a set of heuristics.
     *
     * @param languageCode the language to look up
     * @return the corresponding region code, if any
     */
    @Nullable
    public static String getLanguageRegion(@NonNull String languageCode) {
        return getLanguageRegion(languageCode, null);
    }

    /**
     * Like {@link #getLanguageRegion(String)}, but does not take user preferences
     * and locations into consideration.
     */
    @Nullable
    public static String getDefaultLanguageRegion(@NonNull String languageCode) {
        if (languageCode.length() == 2) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1)) : languageCode;
            int index = Arrays.binarySearch(ISO_639_1_CODES, languageCode);
            if (index < 0 || index >= ISO_639_1_TO_2.length) {
                return null;
            }
            int regionIndex = LANGUAGE_REGION[ISO_639_1_TO_2[index]];
            if (regionIndex != -1) {
                int twoLetterIndex = ISO_3166_2_TO_1[regionIndex];
                if (twoLetterIndex != -1) {
                    return ISO_3166_1_CODES[twoLetterIndex];
                } else {
                    return ISO_3166_2_CODES[regionIndex];
                }
            }
            return null;
        } else if (languageCode.length() == 3) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1))
                    && Character.isLowerCase(languageCode.charAt(2)) : languageCode;
            int index = Arrays.binarySearch(ISO_639_2_CODES, languageCode);
            if (index < 0) {
                return null;
            }
            return getRegionCode(LANGUAGE_REGION[index]);
        }

        assert false : languageCode;
        return null;
    }

    /**
     * Returns the region code for the given language. <b>Note that there can be
     * many regions that speak a given language; this just picks one</b> based
     * on a set of heuristics.
     *
     * @param languageCode the language to look up
     * @return the corresponding region code, if any
     */
    @Nullable
    public static String getLanguageRegion(@NonNull String languageCode,
            @Nullable ResourceRepository resources) {
        // Try to pick one language based on various heuristics:

        // (1) Check to see if the user has deliberately picked a preferred
        //     region for this language with an option. That should always
        //     win. Example: STUDIO_LOCALES="en_GB,pt_PT" says that for English
        //     we should always use the region GB and for Portuguese we should always
        //     use the region PT. Also allow en-GB and en-rGB.
        String option = System.getenv("STUDIO_LOCALES");
        if (option == null) {
            option = System.getProperty("studio.locales");
        }
        if (option != null) {
            for (String regionLocale : Splitter.on(',').trimResults().split(option)) {
                if (SdkUtils.startsWithIgnoreCase(regionLocale, languageCode)) {
                    if (regionLocale.length() == 5 && ((regionLocale.charAt(2) == '_')
                            || regionLocale.charAt(2) == '-')) {
                        return regionLocale.substring(3).toUpperCase(US);
                    } else if (regionLocale.length() == 6 && regionLocale.charAt(2) == '-'
                            && regionLocale.charAt(3) == 'r') {
                        return regionLocale.substring(4).toUpperCase(US);
                    }
                }
            }
        }

        // (2) Check the user's locale; if it happens to be in the same language
        //     as the target language, and it specifies a region, use that region
        Locale locale = Locale.getDefault();
        if (languageCode.equalsIgnoreCase(locale.getLanguage())) {
            String country = locale.getCountry();
            if (!country.isEmpty()) {
                country = country.toUpperCase(US);
                if (country.length() == 2) {
                    return country;
                }
            }
        }

        // Do we have multiple known regions for this locale? If so, try to pick
        // among them based on heuristics.
        List<String> regions = getRelevantRegions(languageCode);
        if (regions.size() > 1) {
            // Some languages are used in a huge number of regions (English is
            // in 90+ regions for example), and similarly, some regions have multiple
            // major languages (Switzerland for example). In these cases we don't want
            // to show a region flag for the local region (e.g. for Switzerland you
            // would see the same flag for German, French, Italian, ...).
            // Therefore, only use region lookup for a subset of languages where
            // we're not sure.
            List<String> relevant = getDisambiguateRegions(languageCode);

            // (3) Check the user's country. The user may not be using the target
            //     language, but if the current country matches one of the relevant
            //     regions, use it.
            String country = locale.getCountry();
            if (!country.isEmpty() && relevant != null) {
                country = country.toUpperCase(US);
                if (country.length() == 2 && regions.contains(country) &&
                        (relevant.isEmpty() || relevant.contains(country))) {
                    return country;
                }
            }

            // (4) Look at the user's network location; if we can resolve
            //     the domain name, the TLD might be an ISO 3166 country code:
            //     http://en.wikipedia.org/wiki/Country_code_top-level_domain
            //     If so, and that country code is in one of the candidate regions,
            //     use it. (Note the exceptions listed in there; we should treat
            //     "uk" as "gb" for ISO code lookup.)
            //
            //   NOTE DONE: It turns out this is tricky. Looking up the current domain
            //     typically requires a network connection, sometimes it can
            //     take seconds, and even the domain name may not be helpful;
            //     it may be for example a .com address.


            // (5) Use the timezone! The timezone can give us a very good clue
            //     about the region. In many cases we can get an exact match,
            //     e.g. if we're looking at the timezone Europe/Lisbon we know
            //     the region is PT. (In the future we could extend this to
            //     not only map from timezone to region code, but to look at
            //     the continent and raw offsets for further clues to guide the
            //     region choice.)
            if (relevant != null) {
                String region = getTimeZoneRegionAlpha2(TimeZone.getDefault());
                if (region == null) {
                    // Unknown timezone: ambiguous and don't attempt to guess
                    return null;
                }
                if (regions.contains(region) && (relevant.isEmpty() || relevant.contains(region))) {
                    return region;
                }
            }

            //
            // (6) Look at installed locales, and limit our options to the regions
            //     found in locales for the given language.
            //     For example, on my system, the LocaleManager provides 90
            //     relevant regions for English, but my system only has 11,
            //     so we can eliminate the remaining 79 from consideration.
            //     (Sadly, it doesn't look like the local locales are sorted
            //     in any way significant for the user, so we can't just assume
            //     that the first locale of the target language is somehow special.)
            Locale candidate = null;
            for (Locale available : Locale.getAvailableLocales()) {
                if (languageCode.equals(available.getLanguage()) &&
                        regions.contains(available.getCountry())) {
                    if (candidate != null) {
                        candidate = null; // more than one match; doesn't help us
                        break;
                    } else {
                        candidate = available;
                    }
                }
            }
            if (candidate != null && relevant != null &&
                    (relevant.isEmpty() || relevant.contains(candidate.getCountry()))) {
                return candidate.getCountry();
            }

            //
            // (7) Consult the project to see which locales are used there.
            //     If for example your project has resources in "en-rUS", it's
            //     unlikely that you intend for "US" to be the region for en
            //     (since that's a region specific overlay) so pick for example GB
            //     instead.
            if (resources != null) {
                ListMultimap<String, com.android.ide.common.res2.ResourceItem> strings = resources
                        .getItems().get(ResourceType.STRING);
                if (strings != null) {
                    Set<String> specified = Sets.newHashSet();
                    for (com.android.ide.common.res2.ResourceItem item : strings.values()) {
                        String qualifiers = item.getQualifiers();
                        if (qualifiers.startsWith(languageCode) && qualifiers.length() == 6
                                && qualifiers.charAt(3) == 'r') {
                            specified.add(qualifiers.substring(4));
                        }
                    }
                    if (!specified.isEmpty()) {
                        // Remove the specified locales from consideration
                        Set<String> all = Sets.newHashSet(regions);
                        all.removeAll(specified);
                        // Only one left?
                        if (all.size() == 1) {
                            return all.iterator().next();
                        }
                    }
                }
            }

            //
            // (8) Give preference to a region that has the same region code
            //     as the language code; this is usually where the language is named
            //     after a region
            char first = Character.toUpperCase(languageCode.charAt(0));
            char second = Character.toUpperCase(languageCode.charAt(1));
            for (String r : regions) {
                if (r.charAt(0) == first && r.charAt(1) == second) {
                    return r;
                }
            }
        } else if (regions.size() == 1) {
            return regions.get(0);
        }

        // Finally just pick the default one
        return getDefaultLanguageRegion(languageCode);
    }

    @Nullable
    private static List<String> getDisambiguateRegions(@NonNull String languageCode) {
        if ("ar".equals(languageCode) || "zh".equals(languageCode)) {
            return Collections.emptyList();
        } else if ("en".equals(languageCode)) {
            return Arrays.asList("US", "GB");
        } else if ("es".equals(languageCode)) {
            return Arrays.asList("MX", "AR", "CL", "CO", "CR", "CU", "DO", "GT", "HN", "NI",
                    "PA", "PY", "SV", "UY", "VE", "ME");
        } else if ("pt".equals(languageCode)) {
            return Arrays.asList("PT", "BR");
        } else if ("ru".equals(languageCode)) {
            return Collections.singletonList("ru");
        } else {
            return null;
        }
    }

    /**
     * Get the region code (either 3166-1 or if necessary, 3166-2) for the given
     * 3166-2 region code
     */
    private static String getRegionCode(int index) {
        if (index != -1) {
            int twoLetterIndex = ISO_3166_2_TO_1[index];
            if (twoLetterIndex != -1) {
                return ISO_3166_1_CODES[twoLetterIndex];
            } else {
                return ISO_3166_2_CODES[index];
            }
        }

        return null;
    }

    /** Returns the relevant regions for the given language, if known. */
    @NonNull
    public static List<String> getRelevantRegions(@NonNull String languageCode) {
        int languageIndex;
        if (languageCode.length() == 2) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1)) : languageCode;
            int index = Arrays.binarySearch(ISO_639_1_CODES, languageCode);
            if (index < 0 || index >= ISO_639_1_TO_2.length) {
                return Collections.emptyList();
            }
            languageIndex = ISO_639_1_TO_2[index];
        } else if (languageCode.length() == 3) {
            assert Character.isLowerCase(languageCode.charAt(0))
                    && Character.isLowerCase(languageCode.charAt(1))
                    && Character.isLowerCase(languageCode.charAt(2)) : languageCode;
            languageIndex = Arrays.binarySearch(ISO_639_2_CODES, languageCode);
            if (languageIndex < 0) {
                return Collections.emptyList();
            }
        } else {
            assert false : languageCode;
            return Collections.emptyList();
        }

        int[] regionIndices = LANGUAGE_REGIONS[languageIndex];
        if (regionIndices == null) { // only returns non-null when there are multiple
            String regionCode = getRegionCode(LANGUAGE_REGION[languageIndex]);
            return regionCode != null ? Collections.singletonList(regionCode)
                    : Collections.<String>emptyList();

        }

        List<String> result = Lists.newArrayListWithExpectedSize(regionIndices.length);
        for (int regionIndex : regionIndices) {
            String regionCode = getRegionCode(regionIndex);
            if (regionCode != null) {
                result.add(regionCode);
            }
        }
        return result;
    }

    /**
     * Returns the corresponding ISO 639 alpha-2 code given an alpha-3 code
     *
     * @param languageCode the ISO 639 alpha-3 code
     * @return the corresponding ISO 639 alpha-2 code, if any
     */
    @Nullable
    public static String getLanguageAlpha2(@NonNull String languageCode) {
        assert languageCode.length() == 3 : languageCode;
        assert Character.isLowerCase(languageCode.charAt(0))
                && Character.isLowerCase(languageCode.charAt(1))
                && Character.isLowerCase(languageCode.charAt(2)) : languageCode;
        int index = Arrays.binarySearch(ISO_639_2_CODES, languageCode);
        if (index < 0) {
            return null;
        }
        int alpha2 = ISO_639_2_TO_1[index];
        if (alpha2 != -1) {
            return ISO_639_1_CODES[alpha2];
        }
        return null;
    }

    /**
     * Returns the corresponding ISO 639 alpha-3 code given an alpha-2 code
     *
     * @param languageCode the ISO 639 alpha-2 code
     * @return the corresponding ISO 639 alpha-3 code, if any
     */
    @Nullable
    public static String getLanguageAlpha3(@NonNull String languageCode) {
        assert languageCode.length() == 2 : languageCode;
        assert Character.isLowerCase(languageCode.charAt(0))
                && Character.isLowerCase(languageCode.charAt(1)) : languageCode;
        int index = Arrays.binarySearch(ISO_639_1_CODES, languageCode);
        if (index < 0) {
            return null;
        }
        int alpha2 = ISO_639_1_TO_2[index];
        if (alpha2 != -1) {
            return ISO_639_2_CODES[alpha2];
        }
        return null;
    }

    /**
     * Returns the corresponding ISO 3166 alpha-2 code given an alpha-3 code
     *
     * @param regionCode the ISO 3166 alpha-3 code
     * @return the corresponding ISO 3166 alpha-2 code, if any
     */
    @Nullable
    public static String getRegionAlpha2(@NonNull String regionCode) {
        assert regionCode.length() == 3 : regionCode;
        assert Character.isUpperCase(regionCode.charAt(0))
                && Character.isUpperCase(regionCode.charAt(1))
                && Character.isUpperCase(regionCode.charAt(2)) : regionCode;
        int index = Arrays.binarySearch(ISO_3166_2_CODES, regionCode);
        if (index < 0) {
            return null;
        }
        int alpha2 = ISO_3166_2_TO_1[index];
        if (alpha2 != -1) {
            return ISO_3166_1_CODES[alpha2];
        }
        return null;
    }

    /**
     * Returns the corresponding ISO 3166 alpha-3 code given an alpha-2 code
     *
     * @param regionCode the ISO 3166 alpha-2 code
     * @return the corresponding ISO 3166 alpha-3 code, if any
     */
    @Nullable
    public static String getRegionAlpha3(@NonNull String regionCode) {
        assert regionCode.length() == 2 : regionCode;
        assert Character.isUpperCase(regionCode.charAt(0))
                && Character.isUpperCase(regionCode.charAt(1)) : regionCode;
        int index = Arrays.binarySearch(ISO_3166_1_CODES, regionCode);
        if (index < 0) {
            return null;
        }
        int alpha2 = ISO_3166_1_TO_2[index];
        if (alpha2 != -1) {
            return ISO_3166_2_CODES[alpha2];
        }
        return null;
    }

    /**
     * Guess the 3-letter region code containing the given time zone
     *
     * @param zone The timezone to look up
     * @return the corresponding 3 letter region code
     */
    @SuppressWarnings("SpellCheckingInspection")
    @Nullable
    @VisibleForTesting
    public static String getTimeZoneRegionAlpha3(@NonNull TimeZone zone) {
        int index = getTimeZoneRegionIndex(zone);
        if (index != -1) {
            return ISO_3166_2_CODES[index];
        }

        return null;
    }

    /**
     * Guess the 2-letter region code containing the given time zone
     *
     * @param zone The timezone to look up
     * @return the corresponding 2 letter region code
     */
    @SuppressWarnings("SpellCheckingInspection")
    @Nullable
    @VisibleForTesting
    public static String getTimeZoneRegionAlpha2(@NonNull TimeZone zone) {
        int index = getTimeZoneRegionIndex(zone);
        if (index != -1) {
            index = ISO_3166_2_TO_1[index];
            if (index != -1) {
                return ISO_3166_1_CODES[index];
            }
        }

        return null;
    }

    // The remainder of this class is generated by generate-locale-data
    // DO NOT EDIT MANUALLY

    private static int getTimeZoneRegionIndex(@NonNull TimeZone zone) {
        // Instead of String#hashCode, use this to ensure stable across platforms
        String id = zone.getID();
        int hashedId = 0;
        for (int i = 0, n = id.length(); i < n; i++) {
            hashedId = 31 * hashedId + id.charAt(i);
        }
        switch (zone.getRawOffset()) {
            case -36000000:
                return 234;
            case -32400000:
                return 234;
            case -28800000:
                switch (hashedId) {
                    case -459287604:
                    case 256046501:
                    case 1647318035:
                    case -1983011822:
                        return 142;
                    case 1389185817:
                    case 900028252:
                    case -347637707:
                    case 364935240:
                    case -2010814355:
                        return 39;
                    default:
                        return 234;
                }
            case -25200000:
                switch (hashedId) {
                    case 202222115:
                    case 611591843:
                    case 2142546433:
                    case 1532263802:
                    case -641163936:
                        return 142;
                    case -1774689070:
                    case -302339179:
                    case -1998145482:
                    case -906910905:
                    case 1544280457:
                    case 1924477936:
                    case 1850095790:
                        return 39;
                    default:
                        return 234;
                }
            case -21600000:
                switch (hashedId) {
                    case -355081471:
                        return 52;
                    case 662067781:
                        return 65;
                    case 268098540:
                        return 92;
                    case -1192934179:
                        return 98;
                    case -496169397:
                        return 164;
                    case -610612331:
                        return 200;
                    case 35870737:
                    case -2089950224:
                        return 42;
                    case 1033313139:
                    case 1360273357:
                    case 958016402:
                    case 1650383341:
                    case -1436528620:
                    case -905842704:
                    case -380253810:
                        return 142;
                    case -1997850159:
                    case 1290869225:
                    case 1793201705:
                    case 1334007082:
                    case 99854508:
                    case 569007676:
                    case 1837303604:
                    case -1616213428:
                    case -1958461186:
                        return 39;
                    default:
                        return 234;
                }
            case -18000000:
                switch (hashedId) {
                    case 1344376451:
                        return 49;
                    case 407688513:
                        return 65;
                    case 1732450137:
                        return 173;
                    case 2039677810:
                        return 175;
                    case 1503655288:
                    case 2111569:
                        return 53;
                    case -615687308:
                    case 42696295:
                    case -1756511823:
                    case 1213658776:
                        return 32;
                    case 1908749375:
                    case -1694184172:
                    case 695184620:
                    case 1356626855:
                    case 622452689:
                    case 977509670:
                    case 151241566:
                    case 1826315056:
                    case -792567293:
                        return 39;
                    default:
                        return 234;
                }
            case -16200000:
                return 238;
            case -14400000:
                switch (hashedId) {
                    case 1501639611:
                        return 8;
                    case 1617469984:
                        return 31;
                    case -432820086:
                        return 63;
                    case 1367207089:
                        return 183;
                    case -611834443:
                    case -2036395347:
                        return 42;
                    case -691236908:
                    case 79506:
                        return 180;
                    case -1680637607:
                    case 1275531960:
                    case -2087755565:
                    case -640330778:
                    case -95289381:
                    case -2011036567:
                        return 39;
                    default:
                        return 32;
                }
            case -12600000:
                return 39;
            case -10800000:
                switch (hashedId) {
                    case 1987071743:
                        return 233;
                    case 1231674648:
                    case -1203975328:
                    case -1203852432:
                    case -615687308:
                    case -1887400619:
                    case 1646238717:
                    case 42696295:
                    case 1793082297:
                    case -1756511823:
                    case -612056498:
                    case -1523781592:
                    case 65649:
                    case 1213658776:
                    case 1213776064:
                        return 32;
                    default:
                        return 8;
                }
            case -7200000:
                return 32;
            case -3600000:
                return 182;
            case 0:
                switch (hashedId) {
                    case -2002672065:
                        return 69;
                    case -3562122:
                        return 137;
                    case 2160119:
                    case 300259341:
                        return 106;
                    case -1722575083:
                    case -1000832298:
                        return 109;
                    case -1677314468:
                    case 518707320:
                    case 794006110:
                        return 182;
                    default:
                        return 79;
                }
            case 3600000:
                switch (hashedId) {
                    case 747709736:
                        return 5;
                    case 804593244:
                        return 15;
                    case 1036497278:
                        return 26;
                    case -516035308:
                        return 18;
                    case 930574244:
                        return 41;
                    case 641004357:
                        return 58;
                    case -862787273:
                        return 62;
                    case -977866396:
                        return 64;
                    case 911784828:
                        return 99;
                    case 1643067635:
                        return 101;
                    case -1407095582:
                        return 111;
                    case 432607731:
                        return 133;
                    case -1834768363:
                        return 148;
                    case 720852545:
                        return 144;
                    case -675325160:
                        return 146;
                    case 1107183657:
                        return 166;
                    case 562540219:
                        return 204;
                    case -1783944015:
                        return 210;
                    case -1262503490:
                        return 209;
                    case -1871032358:
                        return 208;
                    case 1817919522:
                        return 225;
                    case 228701359:
                    case 2079834968:
                        return 59;
                    case 1801750059:
                    case 539516618:
                        return 69;
                    case 68470:
                    case -672549154:
                        return 75;
                    case -1121325742:
                    case 73413677:
                        return 127;
                    case -72083073:
                    case -1407181132:
                        return 167;
                    default:
                        return 179;
                }
            case 7200000:
                switch (hashedId) {
                    case -669373067:
                        return 23;
                    case 1469914287:
                        return 70;
                    case -1854672812:
                        return 72;
                    case 213620546:
                        return 89;
                    case -1678352343:
                        return 114;
                    case -468176592:
                        return 125;
                    case -820952635:
                        return 132;
                    case -1407101538:
                        return 134;
                    case -1305089392:
                        return 188;
                    case 1640682817:
                        return 189;
                    case 1088211684:
                        return 214;
                    case 1587535273:
                        return 246;
                    case -2046172313:
                        return -1;
                    case 540421055:
                    case 660679831:
                        return 57;
                    case -1121325742:
                    case 73413677:
                        return 127;
                    case 65091:
                    case 1801619315:
                    case 66911291:
                        return 66;
                    case 511371267:
                    case -1868494453:
                    case -2095341728:
                        return 110;
                    case 207779975:
                    case -359165265:
                    case -1778564402:
                        return 226;
                    default:
                        return 231;
                }
            case 10800000:
                switch (hashedId) {
                    case -1744032040:
                        return 24;
                    case -675084931:
                        return 28;
                    case -1745250846:
                        return 108;
                    case -195337532:
                        return 123;
                    case -1663926768:
                        return 186;
                    case -5956312:
                        return 191;
                    case 581080470:
                        return 192;
                    case -1439622607:
                        return 245;
                    case -2046172313:
                        return -1;
                    default:
                        return 189;
                }
            case 14400000:
                switch (hashedId) {
                    case -1675354028:
                        return 7;
                    case -138196720:
                        return 171;
                    case -2046172313:
                        return -1;
                    default:
                        return 189;
                }
            case 18000000:
                return 189;
            case 19800000:
                return 104;
            case 21600000:
                switch (hashedId) {
                    case 1958400136:
                    case 88135602:
                        return 43;
                    default:
                        return 189;
                }
            case 25200000:
                switch (hashedId) {
                    case -1738808822:
                        return 218;
                    case 1063310893:
                    case -788096746:
                        return 102;
                    case 1214715332:
                    case 14814128:
                    case 85303:
                        return 241;
                    default:
                        return 189;
                }
            case 28800000:
                switch (hashedId) {
                    case -156810007:
                        return 176;
                    case 43451613:
                        return 228;
                    case 307946178:
                    case 1811257630:
                        return 14;
                    case 404568855:
                    case -390386883:
                        return 96;
                    case -463608032:
                    case -84259736:
                        return 102;
                    case -99068543:
                    case -1778758162:
                        return 157;
                    case 663100500:
                    case -808657565:
                        return 189;
                    case 133428255:
                    case 499614468:
                        return 194;
                    default:
                        return 43;
                }
            case 31500000:
                return 14;
            case 32400000:
                switch (hashedId) {
                    case -996350568:
                        return 102;
                    case -1661964753:
                    case 81326:
                        return 122;
                    case -1660747039:
                    case 73771:
                    case 71341030:
                        return 115;
                    default:
                        return 189;
                }
            case 34200000:
                return 14;
            case 36000000:
                switch (hashedId) {
                    case 1988570398:
                    case -572853474:
                    case 1409241312:
                    case -402306110:
                    case 1755599521:
                    case 1491561941:
                        return 189;
                    default:
                        return 14;
                }
            case 37800000:
                return 14;
            case 39600000:
                switch (hashedId) {
                    case -1609966193:
                        return 14;
                    default:
                        return 189;
                }
            case 43200000:
                switch (hashedId) {
                    case -488745714:
                    case -345416640:
                    case -572853474:
                        return 189;
                    default:
                        return 170;
                }
            case 45900000:
                return 170;
        }
        return -1;
    }

    private static final String[] ISO_639_2_CODES = new String[] {
            "aar", "abk", "ace", "ach", "ada", "ady", "afa", "afh", "afr",
            "agq", "ain", "aka", "akk", "ale", "alg", "alt", "amh", "ang",
            "anp", "apa", "ara", "arc", "arg", "arn", "arp", "art", "arw",
            "asa", "asm", "ast", "ath", "aus", "ava", "ave", "awa", "aym",
            "aze", "bad", "bai", "bak", "bal", "bam", "ban", "bas", "bat",
            "bej", "bel", "bem", "ben", "ber", "bez", "bho", "bih", "bik",
            "bin", "bis", "bla", "bnt", "bod", "bos", "bra", "bre", "brx",
            "btk", "bua", "bug", "bul", "byn", "cad", "cai", "car", "cat",
            "cau", "ceb", "cel", "ces", "cgg", "cha", "chb", "che", "chg",
            "chk", "chm", "chn", "cho", "chp", "chr", "chu", "chv", "chy",
            "cmc", "cop", "cor", "cos", "cpe", "cpf", "cpp", "cre", "crh",
            "crp", "csb", "cus", "cym", "dak", "dan", "dar", "dav", "day",
            "del", "den", "deu", "dgr", "din", "div", "dje", "doi", "dra",
            "dsb", "dua", "dum", "dyo", "dyu", "dzo", "ebu", "efi", "egy",
            "eka", "ell", "elx", "eng", "enm", "epo", "est", "eus", "ewe",
            "ewo", "fan", "fao", "fas", "fat", "fij", "fil", "fin", "fiu",
            "fon", "fra", "frm", "fro", "frr", "frs", "fry", "ful", "fur",
            "gaa", "gay", "gba", "gem", "gez", "gil", "gla", "gle", "glg",
            "glv", "gmh", "goh", "gon", "gor", "got", "grb", "grc", "grn",
            "gsw", "guj", "guz", "gwi", "hai", "hat", "hau", "haw", "heb",
            "her", "hil", "him", "hin", "hit", "hmn", "hmo", "hrv", "hsb",
            "hun", "hup", "hye", "iba", "ibo", "ido", "iii", "ijo", "iku",
            "ile", "ilo", "ina", "inc", "ind", "ine", "inh", "ipk", "ira",
            "iro", "isl", "ita", "jav", "jbo", "jgo", "jmc", "jpn", "jpr",
            "jrb", "kaa", "kab", "kac", "kal", "kam", "kan", "kar", "kas",
            "kat", "kau", "kaw", "kaz", "kbd", "kde", "kea", "kha", "khi",
            "khm", "kho", "khq", "kik", "kin", "kir", "kkj", "kln", "kmb",
            "kok", "kom", "kon", "kor", "kos", "kpe", "krc", "krl", "kro",
            "kru", "ksb", "ksf", "ksh", "kua", "kum", "kur", "kut", "lad",
            "lag", "lah", "lam", "lao", "lat", "lav", "lez", "lim", "lin",
            "lit", "lkt", "lol", "loz", "ltz", "lua", "lub", "lug", "lui",
            "lun", "luo", "lus", "luy", "mad", "mag", "mah", "mai", "mak",
            "mal", "man", "mao", "map", "mar", "mas", "mdf", "mdr", "men",
            "mer", "mfe", "mga", "mgh", "mgo", "mic", "min", "mis", "mkd",
            "mkh", "mlg", "mlt", "mnc", "mni", "mno", "moh", "mon", "mos",
            "msa", "mua", "mul", "mun", "mus", "mwl", "mwr", "mya", "myn",
            "myv", "nah", "nai", "nap", "naq", "nau", "nav", "nbl", "nde",
            "ndo", "nds", "nep", "new", "nia", "nic", "niu", "nld", "nmg",
            "nnh", "nno", "nob", "nog", "non", "nor", "nqo", "nso", "nub",
            "nus", "nwc", "nya", "nym", "nyn", "nyo", "nzi", "oci", "oji",
            "ori", "orm", "osa", "oss", "ota", "oto", "paa", "pag", "pal",
            "pam", "pan", "pap", "pau", "peo", "phi", "phn", "pli", "pol",
            "pon", "por", "pra", "pro", "pus", "que", "raj", "rap", "rar",
            "roa", "rof", "roh", "rom", "ron", "run", "rup", "rus", "rwk",
            "sad", "sag", "sah", "sai", "sal", "sam", "san", "saq", "sas",
            "sat", "sbp", "scn", "sco", "seh", "sel", "sem", "ses", "sga",
            "sgn", "shi", "shn", "sid", "sin", "sio", "sit", "sla", "slk",
            "slv", "sma", "sme", "smi", "smj", "smn", "smo", "sms", "sna",
            "snd", "snk", "sog", "som", "son", "sot", "spa", "sqi", "srd",
            "srn", "srp", "srr", "ssa", "ssw", "suk", "sun", "sus", "sux",
            "swa", "swc", "swe", "syc", "syr", "tah", "tai", "tam", "tat",
            "tel", "tem", "teo", "ter", "tet", "tgk", "tgl", "tha", "tig",
            "tir", "tiv", "tkl", "tlh", "tli", "tmh", "tog", "ton", "tpi",
            "tsi", "tsn", "tso", "tuk", "tum", "tup", "tur", "tut", "tvl",
            "twi", "twq", "tyv", "tzm", "udm", "uga", "uig", "ukr", "umb",
            "und", "urd", "uzb", "vai", "ven", "vie", "vol", "vot", "vun",
            "wae", "wak", "wal", "war", "was", "wen", "wln", "wol", "xal",
            "xho", "xog", "yao", "yap", "yav", "yid", "yor", "ypk", "zap",
            "zbl", "zen", "zgh", "zha", "zho", "znd", "zul", "zun", "zza"

    };

    private static final String[] ISO_639_2_NAMES = new String[] {
            "Afar",                                 // Code aar/aa
            "Abkhazian",                            // Code abk/ab
            "Achinese",                             // Code ace
            "Acoli",                                // Code ach
            "Adangme",                              // Code ada
            "Adyghe; Adygei",                       // Code ady
            "Afro-Asiatic languages",               // Code afa
            "Afrihili",                             // Code afh
            "Afrikaans",                            // Code afr/af
            "Aghem",                                // Code agq
            "Ainu",                                 // Code ain
            "Akan",                                 // Code aka/ak
            "Akkadian",                             // Code akk
            "Aleut",                                // Code ale
            "Algonquian languages",                 // Code alg
            "Southern Altai",                       // Code alt
            "Amharic",                              // Code amh/am
            "English, Old (ca.450-1100)",           // Code ang
            "Angika",                               // Code anp
            "Apache languages",                     // Code apa
            "Arabic",                               // Code ara/ar
            "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)",// Code arc
            "Aragonese",                            // Code arg/an
            "Mapudungun; Mapuche",                  // Code arn
            "Arapaho",                              // Code arp
            "Artificial languages",                 // Code art
            "Arawak",                               // Code arw
            "Asu",                                  // Code asa
            "Assamese",                             // Code asm/as
            "Asturian; Bable; Leonese; Asturleonese",// Code ast
            "Athapascan languages",                 // Code ath
            "Australian languages",                 // Code aus
            "Avaric",                               // Code ava/av
            "Avestan",                              // Code ave/ae
            "Awadhi",                               // Code awa
            "Aymara",                               // Code aym/ay
            "Azerbaijani",                          // Code aze/az
            "Banda languages",                      // Code bad
            "Bamileke languages",                   // Code bai
            "Bashkir",                              // Code bak/ba
            "Baluchi",                              // Code bal
            "Bambara",                              // Code bam/bm
            "Balinese",                             // Code ban
            "Basa",                                 // Code bas
            "Baltic languages",                     // Code bat
            "Beja; Bedawiyet",                      // Code bej
            "Belarusian",                           // Code bel/be
            "Bemba",                                // Code bem
            "Bengali",                              // Code ben/bn
            "Berber languages",                     // Code ber
            "Bena",                                 // Code bez
            "Bhojpuri",                             // Code bho
            "Bihari languages",                     // Code bih/bh
            "Bikol",                                // Code bik
            "Bini; Edo",                            // Code bin
            "Bislama",                              // Code bis/bi
            "Siksika",                              // Code bla
            "Bantu (Other)",                        // Code bnt
            "Tibetan",                              // Code bod/bo
            "Bosnian",                              // Code bos/bs
            "Braj",                                 // Code bra
            "Breton",                               // Code bre/br
            "Bodo",                                 // Code brx
            "Batak languages",                      // Code btk
            "Buriat",                               // Code bua
            "Buginese",                             // Code bug
            "Bulgarian",                            // Code bul/bg
            "Blin; Bilin",                          // Code byn
            "Caddo",                                // Code cad
            "Central American Indian languages",    // Code cai
            "Galibi Carib",                         // Code car
            "Catalan",                              // Code cat/ca
            "Caucasian languages",                  // Code cau
            "Cebuano",                              // Code ceb
            "Celtic languages",                     // Code cel
            "Czech",                                // Code ces/cs
            "Chiga",                                // Code cgg
            "Chamorro",                             // Code cha/ch
            "Chibcha",                              // Code chb
            "Chechen",                              // Code che/ce
            "Chagatai",                             // Code chg
            "Chuukese",                             // Code chk
            "Mari",                                 // Code chm
            "Chinook jargon",                       // Code chn
            "Choctaw",                              // Code cho
            "Chipewyan; Dene Suline",               // Code chp
            "Cherokee",                             // Code chr
            "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic",// Code chu/cu
            "Chuvash",                              // Code chv/cv
            "Cheyenne",                             // Code chy
            "Chamic languages",                     // Code cmc
            "Coptic",                               // Code cop
            "Cornish",                              // Code cor/kw
            "Corsican",                             // Code cos/co
            "Creoles and pidgins, English based",   // Code cpe
            "Creoles and pidgins, French-based ",   // Code cpf
            "Creoles and pidgins, Portuguese-based ",// Code cpp
            "Cree",                                 // Code cre/cr
            "Crimean Tatar; Crimean Turkish",       // Code crh
            "Creoles and pidgins ",                 // Code crp
            "Kashubian",                            // Code csb
            "Cushitic languages",                   // Code cus
            "Welsh",                                // Code cym/cy
            "Dakota",                               // Code dak
            "Danish",                               // Code dan/da
            "Dargwa",                               // Code dar
            "Taita",                                // Code dav
            "Land Dayak languages",                 // Code day
            "Delaware",                             // Code del
            "Slave (Athapascan)",                   // Code den
            "German",                               // Code deu/de
            "Dogrib",                               // Code dgr
            "Dinka",                                // Code din
            "Divehi; Dhivehi; Maldivian",           // Code div/dv
            "Zarma",                                // Code dje
            "Dogri",                                // Code doi
            "Dravidian languages",                  // Code dra
            "Lower Sorbian",                        // Code dsb
            "Duala",                                // Code dua
            "Dutch, Middle (ca.1050-1350)",         // Code dum
            "Jola-Fonyi",                           // Code dyo
            "Dyula",                                // Code dyu
            "Dzongkha",                             // Code dzo/dz
            "Embu",                                 // Code ebu
            "Efik",                                 // Code efi
            "Egyptian (Ancient)",                   // Code egy
            "Ekajuk",                               // Code eka
            "Greek",                                // Code ell/el
            "Elamite",                              // Code elx
            "English",                              // Code eng/en
            "English, Middle (1100-1500)",          // Code enm
            "Esperanto",                            // Code epo/eo
            "Estonian",                             // Code est/et
            "Basque",                               // Code eus/eu
            "Ewe",                                  // Code ewe/ee
            "Ewondo",                               // Code ewo
            "Fang",                                 // Code fan
            "Faroese",                              // Code fao/fo
            "Persian",                              // Code fas/fa
            "Fanti",                                // Code fat
            "Fijian",                               // Code fij/fj
            "Filipino; Pilipino",                   // Code fil
            "Finnish",                              // Code fin/fi
            "Finno-Ugrian languages",               // Code fiu
            "Fon",                                  // Code fon
            "French",                               // Code fra/fr
            "French, Middle (ca.1400-1600)",        // Code frm
            "French, Old (842-ca.1400)",            // Code fro
            "Northern Frisian",                     // Code frr
            "Eastern Frisian",                      // Code frs
            "Western Frisian",                      // Code fry/fy
            "Fulah",                                // Code ful/ff
            "Friulian",                             // Code fur
            "Ga",                                   // Code gaa
            "Gayo",                                 // Code gay
            "Gbaya",                                // Code gba
            "Germanic languages",                   // Code gem
            "Geez",                                 // Code gez
            "Gilbertese",                           // Code gil
            "Scottish Gaelic",                      // Code gla/gd
            "Irish",                                // Code gle/ga
            "Galician",                             // Code glg/gl
            "Manx",                                 // Code glv/gv
            "German, Middle High (ca.1050-1500)",   // Code gmh
            "German, Old High (ca.750-1050)",       // Code goh
            "Gondi",                                // Code gon
            "Gorontalo",                            // Code gor
            "Gothic",                               // Code got
            "Grebo",                                // Code grb
            "Greek, Ancient (to 1453)",             // Code grc
            "Guarani",                              // Code grn/gn
            "Swiss German; Alemannic; Alsatian",    // Code gsw
            "Gujarati",                             // Code guj/gu
            "Gusii",                                // Code guz
            "Gwich'in",                             // Code gwi
            "Haida",                                // Code hai
            "Haitian; Haitian Creole",              // Code hat/ht
            "Hausa",                                // Code hau/ha
            "Hawaiian",                             // Code haw
            "Hebrew",                               // Code heb/iw
            "Herero",                               // Code her/hz
            "Hiligaynon",                           // Code hil
            "Himachali languages; Western Pahari languages",// Code him
            "Hindi",                                // Code hin/hi
            "Hittite",                              // Code hit
            "Hmong; Mong",                          // Code hmn
            "Hiri Motu",                            // Code hmo/ho
            "Croatian",                             // Code hrv/hr
            "Upper Sorbian",                        // Code hsb
            "Hungarian",                            // Code hun/hu
            "Hupa",                                 // Code hup
            "Armenian",                             // Code hye/hy
            "Iban",                                 // Code iba
            "Igbo",                                 // Code ibo/ig
            "Ido",                                  // Code ido/io
            "Sichuan Yi",                           // Code iii/ii
            "Ijo languages",                        // Code ijo
            "Inuktitut",                            // Code iku/iu
            "Interlingue; Occidental",              // Code ile/ie
            "Iloko",                                // Code ilo
            "Interlingua (International Auxiliary Language Association)",// Code ina/ia
            "Indic languages",                      // Code inc
            "Indonesian",                           // Code ind/in
            "Indo-European languages",              // Code ine
            "Ingush",                               // Code inh
            "Inupiaq",                              // Code ipk/ik
            "Iranian languages",                    // Code ira
            "Iroquoian languages",                  // Code iro
            "Icelandic",                            // Code isl/is
            "Italian",                              // Code ita/it
            "Javanese",                             // Code jav/jv
            "Lojban",                               // Code jbo
            "Ngomba",                               // Code jgo
            "Machame",                              // Code jmc
            "Japanese",                             // Code jpn/ja
            "Judeo-Persian",                        // Code jpr
            "Judeo-Arabic",                         // Code jrb
            "Kara-Kalpak",                          // Code kaa
            "Kabyle",                               // Code kab
            "Kachin; Jingpho",                      // Code kac
            "Kalaallisut",                          // Code kal/kl
            "Kamba",                                // Code kam
            "Kannada",                              // Code kan/kn
            "Karen languages",                      // Code kar
            "Kashmiri",                             // Code kas/ks
            "Georgian",                             // Code kat/ka
            "Kanuri",                               // Code kau/kr
            "Kawi",                                 // Code kaw
            "Kazakh",                               // Code kaz/kk
            "Kabardian",                            // Code kbd
            "Makonde",                              // Code kde
            "Kabuverdianu",                         // Code kea
            "Khasi",                                // Code kha
            "Khoisan languages",                    // Code khi
            "Khmer",                                // Code khm/km
            "Khotanese; Sakan",                     // Code kho
            "Koyra Chiini",                         // Code khq
            "Kikuyu",                               // Code kik/ki
            "Kinyarwanda",                          // Code kin/rw
            "Kyrgyz",                               // Code kir/ky
            "Kako",                                 // Code kkj
            "Kalenjin",                             // Code kln
            "Kimbundu",                             // Code kmb
            "Konkani",                              // Code kok
            "Komi",                                 // Code kom/kv
            "Kongo",                                // Code kon/kg
            "Korean",                               // Code kor/ko
            "Kosraean",                             // Code kos
            "Kpelle",                               // Code kpe
            "Karachay-Balkar",                      // Code krc
            "Karelian",                             // Code krl
            "Kru languages",                        // Code kro
            "Kurukh",                               // Code kru
            "Shambala",                             // Code ksb
            "Bafia",                                // Code ksf
            "Colognian",                            // Code ksh
            "Kuanyama; Kwanyama",                   // Code kua/kj
            "Kumyk",                                // Code kum
            "Kurdish",                              // Code kur/ku
            "Kutenai",                              // Code kut
            "Ladino",                               // Code lad
            "Langi",                                // Code lag
            "Lahnda",                               // Code lah
            "Lamba",                                // Code lam
            "Lao",                                  // Code lao/lo
            "Latin",                                // Code lat/la
            "Latvian",                              // Code lav/lv
            "Lezghian",                             // Code lez
            "Limburgan; Limburger; Limburgish",     // Code lim/li
            "Lingala",                              // Code lin/ln
            "Lithuanian",                           // Code lit/lt
            "Lakota",                               // Code lkt
            "Mongo",                                // Code lol
            "Lozi",                                 // Code loz
            "Luxembourgish",                        // Code ltz/lb
            "Luba-Lulua",                           // Code lua
            "Luba-Katanga",                         // Code lub/lu
            "Ganda",                                // Code lug/lg
            "Luiseno",                              // Code lui
            "Lunda",                                // Code lun
            "Luo (Kenya and Tanzania)",             // Code luo
            "Lushai",                               // Code lus
            "Luyia",                                // Code luy
            "Madurese",                             // Code mad
            "Magahi",                               // Code mag
            "Marshallese",                          // Code mah/mh
            "Maithili",                             // Code mai
            "Makasar",                              // Code mak
            "Malayalam",                            // Code mal/ml
            "Mandingo",                             // Code man
            "Maori",                                // Code mao/mi
            "Austronesian languages",               // Code map
            "Marathi",                              // Code mar/mr
            "Masai",                                // Code mas
            "Moksha",                               // Code mdf
            "Mandar",                               // Code mdr
            "Mende",                                // Code men
            "Meru",                                 // Code mer
            "Morisyen",                             // Code mfe
            "Irish, Middle (900-1200)",             // Code mga
            "Makhuwa-Meetto",                       // Code mgh
            "Meta\u02bc",                           // Code mgo
            "Mi'kmaq; Micmac",                      // Code mic
            "Minangkabau",                          // Code min
            "Uncoded languages",                    // Code mis
            "Macedonian",                           // Code mkd/mk
            "Mon-Khmer languages",                  // Code mkh
            "Malagasy",                             // Code mlg/mg
            "Maltese",                              // Code mlt/mt
            "Manchu",                               // Code mnc
            "Manipuri",                             // Code mni
            "Manobo languages",                     // Code mno
            "Mohawk",                               // Code moh
            "Mongolian",                            // Code mon/mn
            "Mossi",                                // Code mos
            "Malay",                                // Code msa/ms
            "Mundang",                              // Code mua
            "Multiple languages",                   // Code mul
            "Munda languages",                      // Code mun
            "Creek",                                // Code mus
            "Mirandese",                            // Code mwl
            "Marwari",                              // Code mwr
            "Burmese",                              // Code mya/my
            "Mayan languages",                      // Code myn
            "Erzya",                                // Code myv
            "Nahuatl languages",                    // Code nah
            "North American Indian languages",      // Code nai
            "Neapolitan",                           // Code nap
            "Nama",                                 // Code naq
            "Nauru",                                // Code nau/na
            "Navajo; Navaho",                       // Code nav/nv
            "Ndebele, South; South Ndebele",        // Code nbl/nr
            "North Ndebele",                        // Code nde/nd
            "Ndonga",                               // Code ndo/ng
            "Low German; Low Saxon; German, Low; Saxon, Low",// Code nds
            "Nepali",                               // Code nep/ne
            "Nepal Bhasa; Newari",                  // Code new
            "Nias",                                 // Code nia
            "Niger-Kordofanian languages",          // Code nic
            "Niuean",                               // Code niu
            "Dutch",                                // Code nld/nl
            "Kwasio",                               // Code nmg
            "Ngiemboon",                            // Code nnh
            "Norwegian Nynorsk",                    // Code nno/nn
            "Norwegian Bokm\u00e5l",                // Code nob/nb
            "Nogai",                                // Code nog
            "Norse, Old",                           // Code non
            "Norwegian",                            // Code nor/no
            "N'Ko",                                 // Code nqo
            "Pedi; Sepedi; Northern Sotho",         // Code nso
            "Nubian languages",                     // Code nub
            "Nuer",                                 // Code nus
            "Classical Newari; Old Newari; Classical Nepal Bhasa",// Code nwc
            "Chichewa; Chewa; Nyanja",              // Code nya/ny
            "Nyamwezi",                             // Code nym
            "Nyankole",                             // Code nyn
            "Nyoro",                                // Code nyo
            "Nzima",                                // Code nzi
            "Occitan (post 1500); Proven\u00e7al",  // Code oci/oc
            "Ojibwa",                               // Code oji/oj
            "Oriya",                                // Code ori/or
            "Oromo",                                // Code orm/om
            "Osage",                                // Code osa
            "Ossetic",                              // Code oss/os
            "Turkish, Ottoman (1500-1928)",         // Code ota
            "Otomian languages",                    // Code oto
            "Papuan languages",                     // Code paa
            "Pangasinan",                           // Code pag
            "Pahlavi",                              // Code pal
            "Pampanga; Kapampangan",                // Code pam
            "Punjabi",                              // Code pan/pa
            "Papiamento",                           // Code pap
            "Palauan",                              // Code pau
            "Persian, Old (ca.600-400 B.C.)",       // Code peo
            "Philippine languages",                 // Code phi
            "Phoenician",                           // Code phn
            "Pali",                                 // Code pli/pi
            "Polish",                               // Code pol/pl
            "Pohnpeian",                            // Code pon
            "Portuguese",                           // Code por/pt
            "Prakrit languages",                    // Code pra
            "Proven\u00e7al, Old (to 1500)",        // Code pro
            "Pashto",                               // Code pus/ps
            "Quechua",                              // Code que/qu
            "Rajasthani",                           // Code raj
            "Rapanui",                              // Code rap
            "Rarotongan; Cook Islands Maori",       // Code rar
            "Romance languages",                    // Code roa
            "Rombo",                                // Code rof
            "Romansh",                              // Code roh/rm
            "Romany",                               // Code rom
            "Romanian",                             // Code ron/ro
            "Rundi",                                // Code run/rn
            "Aromanian; Arumanian; Macedo-Romanian",// Code rup
            "Russian",                              // Code rus/ru
            "Rwa",                                  // Code rwk
            "Sandawe",                              // Code sad
            "Sango",                                // Code sag/sg
            "Yakut",                                // Code sah
            "South American Indian (Other)",        // Code sai
            "Salishan languages",                   // Code sal
            "Samaritan Aramaic",                    // Code sam
            "Sanskrit",                             // Code san/sa
            "Samburu",                              // Code saq
            "Sasak",                                // Code sas
            "Santali",                              // Code sat
            "Sangu",                                // Code sbp
            "Sicilian",                             // Code scn
            "Scots",                                // Code sco
            "Sena",                                 // Code seh
            "Selkup",                               // Code sel
            "Semitic languages",                    // Code sem
            "Koyraboro Senni",                      // Code ses
            "Irish, Old (to 900)",                  // Code sga
            "Sign Languages",                       // Code sgn
            "Tachelhit",                            // Code shi
            "Shan",                                 // Code shn
            "Sidamo",                               // Code sid
            "Sinhala",                              // Code sin/si
            "Siouan languages",                     // Code sio
            "Sino-Tibetan languages",               // Code sit
            "Slavic languages",                     // Code sla
            "Slovak",                               // Code slk/sk
            "Slovenian",                            // Code slv/sl
            "Southern Sami",                        // Code sma
            "Northern Sami",                        // Code sme/se
            "Sami languages",                       // Code smi
            "Lule Sami",                            // Code smj
            "Inari Sami",                           // Code smn
            "Samoan",                               // Code smo/sm
            "Skolt Sami",                           // Code sms
            "Shona",                                // Code sna/sn
            "Sindhi",                               // Code snd/sd
            "Soninke",                              // Code snk
            "Sogdian",                              // Code sog
            "Somali",                               // Code som/so
            "Songhai languages",                    // Code son
            "Sotho, Southern",                      // Code sot/st
            "Spanish",                              // Code spa/es
            "Albanian",                             // Code sqi/sq
            "Sardinian",                            // Code srd/sc
            "Sranan Tongo",                         // Code srn
            "Serbian",                              // Code srp/sr
            "Serer",                                // Code srr
            "Nilo-Saharan languages",               // Code ssa
            "Swati",                                // Code ssw/ss
            "Sukuma",                               // Code suk
            "Sundanese",                            // Code sun/su
            "Susu",                                 // Code sus
            "Sumerian",                             // Code sux
            "Swahili",                              // Code swa/sw
            "Congo Swahili",                        // Code swc
            "Swedish",                              // Code swe/sv
            "Classical Syriac",                     // Code syc
            "Syriac",                               // Code syr
            "Tahitian",                             // Code tah/ty
            "Tai languages",                        // Code tai
            "Tamil",                                // Code tam/ta
            "Tatar",                                // Code tat/tt
            "Telugu",                               // Code tel/te
            "Timne",                                // Code tem
            "Teso",                                 // Code teo
            "Tereno",                               // Code ter
            "Tetum",                                // Code tet
            "Tajik",                                // Code tgk/tg
            "Tagalog",                              // Code tgl/tl
            "Thai",                                 // Code tha/th
            "Tigre",                                // Code tig
            "Tigrinya",                             // Code tir/ti
            "Tiv",                                  // Code tiv
            "Tokelau",                              // Code tkl
            "Klingon; tlhIngan-Hol",                // Code tlh
            "Tlingit",                              // Code tli
            "Tamashek",                             // Code tmh
            "Tonga (Nyasa)",                        // Code tog
            "Tongan",                               // Code ton/to
            "Tok Pisin",                            // Code tpi
            "Tsimshian",                            // Code tsi
            "Tswana",                               // Code tsn/tn
            "Tsonga",                               // Code tso/ts
            "Turkmen",                              // Code tuk/tk
            "Tumbuka",                              // Code tum
            "Tupi languages",                       // Code tup
            "Turkish",                              // Code tur/tr
            "Altaic languages",                     // Code tut
            "Tuvalu",                               // Code tvl
            "Twi",                                  // Code twi/tw
            "Tasawaq",                              // Code twq
            "Tuvinian",                             // Code tyv
            "Central Atlas Tamazight",              // Code tzm
            "Udmurt",                               // Code udm
            "Ugaritic",                             // Code uga
            "Uyghur",                               // Code uig/ug
            "Ukrainian",                            // Code ukr/uk
            "Umbundu",                              // Code umb
            "Undetermined",                         // Code und
            "Urdu",                                 // Code urd/ur
            "Uzbek",                                // Code uzb/uz
            "Vai",                                  // Code vai
            "Venda",                                // Code ven/ve
            "Vietnamese",                           // Code vie/vi
            "Volap\u00fck",                         // Code vol/vo
            "Votic",                                // Code vot
            "Vunjo",                                // Code vun
            "Walser",                               // Code wae
            "Wakashan languages",                   // Code wak
            "Walamo",                               // Code wal
            "Waray",                                // Code war
            "Washo",                                // Code was
            "Sorbian languages",                    // Code wen
            "Walloon",                              // Code wln/wa
            "Wolof",                                // Code wol/wo
            "Kalmyk; Oirat",                        // Code xal
            "Xhosa",                                // Code xho/xh
            "Soga",                                 // Code xog
            "Yao",                                  // Code yao
            "Yapese",                               // Code yap
            "Yangben",                              // Code yav
            "Yiddish",                              // Code yid/ji
            "Yoruba",                               // Code yor/yo
            "Yupik languages",                      // Code ypk
            "Zapotec",                              // Code zap
            "Blissymbols; Blissymbolics; Bliss",    // Code zbl
            "Zenaga",                               // Code zen
            "Standard Moroccan Tamazight",          // Code zgh
            "Zhuang; Chuang",                       // Code zha/za
            "Chinese",                              // Code zho/zh
            "Zande languages",                      // Code znd
            "Zulu",                                 // Code zul/zu
            "Zuni",                                 // Code zun
            "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki"// Code zza
    };

    private static final String[] ISO_639_1_CODES = new String[] {
            "aa", "ab", "ae", "af", "ak", "am", "an", "ar", "as", "av", "ay",
            "az", "ba", "be", "bg", "bh", "bi", "bm", "bn", "bo", "br", "bs",
            "ca", "ce", "ch", "co", "cr", "cs", "cu", "cv", "cy", "da", "de",
            "dv", "dz", "ee", "el", "en", "eo", "es", "et", "eu", "fa", "ff",
            "fi", "fj", "fo", "fr", "fy", "ga", "gd", "gl", "gn", "gu", "gv",
            "ha", "he", "hi", "ho", "hr", "ht", "hu", "hy", "hz", "ia", "id",
            "ie", "ig", "ii", "ik", "in", "io", "is", "it", "iu", "iw", "ja",
            "ji", "jv", "ka", "kg", "ki", "kj", "kk", "kl", "km", "kn", "ko",
            "kr", "ks", "ku", "kv", "kw", "ky", "la", "lb", "lg", "li", "ln",
            "lo", "lt", "lu", "lv", "mg", "mh", "mi", "mk", "ml", "mn", "mr",
            "ms", "mt", "my", "na", "nb", "nd", "ne", "ng", "nl", "nn", "no",
            "nr", "nv", "ny", "oc", "oj", "om", "or", "os", "pa", "pi", "pl",
            "ps", "pt", "qu", "rm", "rn", "ro", "ru", "rw", "sa", "sc", "sd",
            "se", "sg", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", "ss",
            "st", "su", "sv", "sw", "ta", "te", "tg", "th", "ti", "tk", "tl",
            "tn", "to", "tr", "ts", "tt", "tw", "ty", "ug", "uk", "ur", "uz",
            "ve", "vi", "vo", "wa", "wo", "xh", "yi", "yo", "za", "zh", "zu"

    };

    // Each element corresponds to an ISO 639-1 code, and contains the index
    // for the corresponding ISO 639-2 code
    private static final int[] ISO_639_1_TO_2 = new int[] {
            0,   1,  33,   8,  11,  16,  22,  20,  28,  32,  35,
            36,  39,  46,  66,  52,  55,  41,  48,  58,  61,  59,
            71,  79,  77,  93,  97,  75,  87,  88, 102, 104, 110,
            113, 122, 134, 127, 129, 131, 438, 132, 133, 138, 151,
            142, 140, 137, 145, 150, 160, 159, 161, 170, 172, 162,
            177, 179, 183, 186, 187, 176, 189, 191, 180, 200, 202,
            198, 193, 195, 205, 202, 194, 208, 209, 197, 179, 214,
            518, 210, 225, 245, 237, 256, 228, 220, 234, 222, 246,
            226, 224, 258, 244,  92, 239, 265, 274, 277, 268, 269,
            264, 270, 276, 266, 307, 285, 290, 305, 288, 313, 292,
            315, 308, 322, 329, 344, 332, 335, 333, 340, 343, 347,
            331, 330, 353, 358, 359, 361, 360, 363, 370, 376, 377,
            382, 379, 383, 389, 392, 391, 394, 238, 402, 440, 432,
            425, 397, 418, 422, 423, 429, 431, 435, 439, 442, 445,
            437, 447, 452, 450, 457, 459, 464, 466, 468, 480, 465,
            478, 475, 483, 479, 458, 486, 455, 492, 493, 496, 497,
            499, 500, 501, 510, 511, 513, 518, 519, 525, 526, 528

    };

    // Each element corresponds to an ISO 639-2 code, and contains the index
    // for the corresponding ISO 639-1 code, or -1 if not represented
    private static final int[] ISO_639_2_TO_1 = new int[] {
            0,   1,  -1,  -1,  -1,  -1,  -1,  -1,   3,  -1,  -1,
            4,  -1,  -1,  -1,  -1,   5,  -1,  -1,  -1,   7,  -1,
            6,  -1,  -1,  -1,  -1,  -1,   8,  -1,  -1,  -1,   9,
            2,  -1,  10,  11,  -1,  -1,  12,  -1,  17,  -1,  -1,
            -1,  -1,  13,  -1,  18,  -1,  -1,  -1,  15,  -1,  -1,
            16,  -1,  -1,  19,  21,  -1,  20,  -1,  -1,  -1,  -1,
            14,  -1,  -1,  -1,  -1,  22,  -1,  -1,  -1,  27,  -1,
            24,  -1,  23,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  28,
            29,  -1,  -1,  -1,  92,  25,  -1,  -1,  -1,  26,  -1,
            -1,  -1,  -1,  30,  -1,  31,  -1,  -1,  -1,  -1,  -1,
            32,  -1,  -1,  33,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
            -1,  34,  -1,  -1,  -1,  -1,  36,  -1,  37,  -1,  38,
            40,  41,  35,  -1,  -1,  46,  42,  -1,  45,  -1,  44,
            -1,  -1,  47,  -1,  -1,  -1,  -1,  48,  43,  -1,  -1,
            -1,  -1,  -1,  -1,  -1,  50,  49,  51,  54,  -1,  -1,
            -1,  -1,  -1,  -1,  -1,  52,  -1,  53,  -1,  -1,  -1,
            60,  55,  -1,  75,  63,  -1,  -1,  57,  -1,  -1,  58,
            59,  -1,  61,  -1,  62,  -1,  67,  71,  68,  -1,  74,
            66,  -1,  64,  -1,  70,  -1,  -1,  69,  -1,  -1,  72,
            73,  78,  -1,  -1,  -1,  76,  -1,  -1,  -1,  -1,  -1,
            84,  -1,  86,  -1,  89,  79,  88,  -1,  83,  -1,  -1,
            -1,  -1,  -1,  85,  -1,  -1,  81, 139,  93,  -1,  -1,
            -1,  -1,  91,  80,  87,  -1,  -1,  -1,  -1,  -1,  -1,
            -1,  -1,  -1,  82,  -1,  90,  -1,  -1,  -1,  -1,  -1,
            99,  94, 102,  -1,  97,  98, 100,  -1,  -1,  -1,  95,
            -1, 101,  96,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 104,
            -1,  -1, 107,  -1, 105,  -1, 109,  -1,  -1,  -1,  -1,
            -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 106,  -1, 103,
            111,  -1,  -1,  -1,  -1, 108,  -1, 110,  -1,  -1,  -1,
            -1,  -1,  -1, 112,  -1,  -1,  -1,  -1,  -1,  -1, 113,
            122, 121, 115, 117,  -1, 116,  -1,  -1,  -1,  -1, 118,
            -1,  -1, 119, 114,  -1,  -1, 120,  -1,  -1,  -1,  -1,
            -1, 123,  -1,  -1,  -1,  -1, 124, 125, 127, 126,  -1,
            128,  -1,  -1,  -1,  -1,  -1,  -1, 129,  -1,  -1,  -1,
            -1,  -1, 130, 131,  -1, 133,  -1,  -1, 132, 134,  -1,
            -1,  -1,  -1,  -1, 135,  -1, 137, 136,  -1, 138,  -1,
            -1, 144,  -1,  -1,  -1,  -1, 140,  -1,  -1,  -1,  -1,
            -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
            145,  -1,  -1,  -1, 146, 147,  -1, 143,  -1,  -1,  -1,
            148,  -1, 149, 142,  -1,  -1, 150,  -1, 154,  39, 151,
            141,  -1, 152,  -1,  -1, 153,  -1, 155,  -1,  -1, 157,
            -1, 156,  -1,  -1, 171,  -1, 158, 169, 159,  -1,  -1,
            -1,  -1, 160, 164, 161,  -1, 162,  -1,  -1,  -1,  -1,
            -1,  -1, 166,  -1,  -1, 165, 168, 163,  -1,  -1, 167,
            -1,  -1, 170,  -1,  -1,  -1,  -1,  -1, 172, 173,  -1,
            -1, 174, 175,  -1, 176, 177, 178,  -1,  -1,  -1,  -1,
            -1,  -1,  -1,  -1, 179, 180,  -1, 181,  -1,  -1,  -1,
            -1,  77, 183,  -1,  -1,  -1,  -1,  -1, 184, 185,  -1,
            186,  -1,  -1
    };
    private static final String[] ISO_3166_2_CODES = new String[] {
            "ABW", "AFG", "AGO", "AIA", "ALA", "ALB", "AND", "ARE", "ARG",
            "ARM", "ASM", "ATA", "ATF", "ATG", "AUS", "AUT", "AZE", "BDI",
            "BEL", "BEN", "BES", "BFA", "BGD", "BGR", "BHR", "BHS", "BIH",
            "BLM", "BLR", "BLZ", "BMU", "BOL", "BRA", "BRB", "BRN", "BTN",
            "BVT", "BWA", "CAF", "CAN", "CCK", "CHE", "CHL", "CHN", "CIV",
            "CMR", "COD", "COG", "COK", "COL", "COM", "CPV", "CRI", "CUB",
            "CUW", "CXR", "CYM", "CYP", "CZE", "DEU", "DJI", "DMA", "DNK",
            "DOM", "DZA", "ECU", "EGY", "ERI", "ESH", "ESP", "EST", "ETH",
            "FIN", "FJI", "FLK", "FRA", "FRO", "FSM", "GAB", "GBR", "GEO",
            "GGY", "GHA", "GIB", "GIN", "GLP", "GMB", "GNB", "GNQ", "GRC",
            "GRD", "GRL", "GTM", "GUF", "GUM", "GUY", "HKG", "HMD", "HND",
            "HRV", "HTI", "HUN", "IDN", "IMN", "IND", "IOT", "IRL", "IRN",
            "IRQ", "ISL", "ISR", "ITA", "JAM", "JEY", "JOR", "JPN", "KAZ",
            "KEN", "KGZ", "KHM", "KIR", "KNA", "KOR", "KWT", "LAO", "LBN",
            "LBR", "LBY", "LCA", "LIE", "LKA", "LSO", "LTU", "LUX", "LVA",
            "MAC", "MAF", "MAR", "MCO", "MDA", "MDG", "MDV", "MEX", "MHL",
            "MKD", "MLI", "MLT", "MMR", "MNE", "MNG", "MNP", "MOZ", "MRT",
            "MSR", "MTQ", "MUS", "MWI", "MYS", "MYT", "NAM", "NCL", "NER",
            "NFK", "NGA", "NIC", "NIU", "NLD", "NOR", "NPL", "NRU", "NZL",
            "OMN", "PAK", "PAN", "PCN", "PER", "PHL", "PLW", "PNG", "POL",
            "PRI", "PRK", "PRT", "PRY", "PSE", "PYF", "QAT", "REU", "ROU",
            "RUS", "RWA", "SAU", "SDN", "SEN", "SGP", "SGS", "SHN", "SJM",
            "SLB", "SLE", "SLV", "SMR", "SOM", "SPM", "SRB", "SSD", "STP",
            "SUR", "SVK", "SVN", "SWE", "SWZ", "SXM", "SYC", "SYR", "TCA",
            "TCD", "TGO", "THA", "TJK", "TKL", "TKM", "TLS", "TON", "TTO",
            "TUN", "TUR", "TUV", "TWN", "TZA", "UGA", "UKR", "UMI", "URY",
            "USA", "UZB", "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT",
            "WLF", "WSM", "YEM", "ZAF", "ZMB", "ZWE"
    };

    private static final String[] ISO_3166_2_NAMES = new String[] {
            "Aruba",                                // Code ABW/AW
            "Afghanistan",                          // Code AFG/AF
            "Angola",                               // Code AGO/AO
            "Anguilla",                             // Code AIA/AI
            "\u00c5land Islands",                   // Code ALA/AX
            "Albania",                              // Code ALB/AL
            "Andorra",                              // Code AND/AD
            "United Arab Emirates",                 // Code ARE/AE
            "Argentina",                            // Code ARG/AR
            "Armenia",                              // Code ARM/AM
            "American Samoa",                       // Code ASM/AS
            "Antarctica",                           // Code ATA/AQ
            "French Southern Territories",          // Code ATF/TF
            "Antigua & Barbuda",                    // Code ATG/AG
            "Australia",                            // Code AUS/AU
            "Austria",                              // Code AUT/AT
            "Azerbaijan",                           // Code AZE/AZ
            "Burundi",                              // Code BDI/BI
            "Belgium",                              // Code BEL/BE
            "Benin",                                // Code BEN/BJ
            "Caribbean Netherlands",                // Code BES/BQ
            "Burkina Faso",                         // Code BFA/BF
            "Bangladesh",                           // Code BGD/BD
            "Bulgaria",                             // Code BGR/BG
            "Bahrain",                              // Code BHR/BH
            "Bahamas",                              // Code BHS/BS
            "Bosnia & Herzegovina",                 // Code BIH/BA
            "St. Barth\u00e9lemy",                  // Code BLM/BL
            "Belarus",                              // Code BLR/BY
            "Belize",                               // Code BLZ/BZ
            "Bermuda",                              // Code BMU/BM
            "Bolivia",                              // Code BOL/BO
            "Brazil",                               // Code BRA/BR
            "Barbados",                             // Code BRB/BB
            "Brunei",                               // Code BRN/BN
            "Bhutan",                               // Code BTN/BT
            "Bouvet Island",                        // Code BVT/BV
            "Botswana",                             // Code BWA/BW
            "Central African Republic",             // Code CAF/CF
            "Canada",                               // Code CAN/CA
            "Cocos (Keeling) Islands",              // Code CCK/CC
            "Switzerland",                          // Code CHE/CH
            "Chile",                                // Code CHL/CL
            "China",                                // Code CHN/CN
            "C\u00f4te d\u2019Ivoire",              // Code CIV/CI
            "Cameroon",                             // Code CMR/CM
            "Congo - Kinshasa",                     // Code COD/CD
            "Congo - Brazzaville",                  // Code COG/CG
            "Cook Islands",                         // Code COK/CK
            "Colombia",                             // Code COL/CO
            "Comoros",                              // Code COM/KM
            "Cape Verde",                           // Code CPV/CV
            "Costa Rica",                           // Code CRI/CR
            "Cuba",                                 // Code CUB/CU
            "Cura\u00e7ao",                         // Code CUW/CW
            "Christmas Island",                     // Code CXR/CX
            "Cayman Islands",                       // Code CYM/KY
            "Cyprus",                               // Code CYP/CY
            "Czech Republic",                       // Code CZE/CZ
            "Germany",                              // Code DEU/DE
            "Djibouti",                             // Code DJI/DJ
            "Dominica",                             // Code DMA/DM
            "Denmark",                              // Code DNK/DK
            "Dominican Republic",                   // Code DOM/DO
            "Algeria",                              // Code DZA/DZ
            "Ecuador",                              // Code ECU/EC
            "Egypt",                                // Code EGY/EG
            "Eritrea",                              // Code ERI/ER
            "Western Sahara",                       // Code ESH/EH
            "Spain",                                // Code ESP/ES
            "Estonia",                              // Code EST/EE
            "Ethiopia",                             // Code ETH/ET
            "Finland",                              // Code FIN/FI
            "Fiji",                                 // Code FJI/FJ
            "Falkland Islands",                     // Code FLK/FK
            "France",                               // Code FRA/FR
            "Faroe Islands",                        // Code FRO/FO
            "Micronesia",                           // Code FSM/FM
            "Gabon",                                // Code GAB/GA
            "United Kingdom",                       // Code GBR/GB
            "Georgia",                              // Code GEO/GE
            "Guernsey",                             // Code GGY/GG
            "Ghana",                                // Code GHA/GH
            "Gibraltar",                            // Code GIB/GI
            "Guinea",                               // Code GIN/GN
            "Guadeloupe",                           // Code GLP/GP
            "Gambia",                               // Code GMB/GM
            "Guinea-Bissau",                        // Code GNB/GW
            "Equatorial Guinea",                    // Code GNQ/GQ
            "Greece",                               // Code GRC/GR
            "Grenada",                              // Code GRD/GD
            "Greenland",                            // Code GRL/GL
            "Guatemala",                            // Code GTM/GT
            "French Guiana",                        // Code GUF/GF
            "Guam",                                 // Code GUM/GU
            "Guyana",                               // Code GUY/GY
            "Hong Kong SAR China",                  // Code HKG/HK
            "Heard Island and McDonald Islands",    // Code HMD/HM
            "Honduras",                             // Code HND/HN
            "Croatia",                              // Code HRV/HR
            "Haiti",                                // Code HTI/HT
            "Hungary",                              // Code HUN/HU
            "Indonesia",                            // Code IDN/ID
            "Isle of Man",                          // Code IMN/IM
            "India",                                // Code IND/IN
            "British Indian Ocean Territory",       // Code IOT/IO
            "Ireland",                              // Code IRL/IE
            "Iran",                                 // Code IRN/IR
            "Iraq",                                 // Code IRQ/IQ
            "Iceland",                              // Code ISL/IS
            "Israel",                               // Code ISR/IL
            "Italy",                                // Code ITA/IT
            "Jamaica",                              // Code JAM/JM
            "Jersey",                               // Code JEY/JE
            "Jordan",                               // Code JOR/JO
            "Japan",                                // Code JPN/JP
            "Kazakhstan",                           // Code KAZ/KZ
            "Kenya",                                // Code KEN/KE
            "Kyrgyzstan",                           // Code KGZ/KG
            "Cambodia",                             // Code KHM/KH
            "Kiribati",                             // Code KIR/KI
            "St. Kitts & Nevis",                    // Code KNA/KN
            "South Korea",                          // Code KOR/KR
            "Kuwait",                               // Code KWT/KW
            "Laos",                                 // Code LAO/LA
            "Lebanon",                              // Code LBN/LB
            "Liberia",                              // Code LBR/LR
            "Libya",                                // Code LBY/LY
            "St. Lucia",                            // Code LCA/LC
            "Liechtenstein",                        // Code LIE/LI
            "Sri Lanka",                            // Code LKA/LK
            "Lesotho",                              // Code LSO/LS
            "Lithuania",                            // Code LTU/LT
            "Luxembourg",                           // Code LUX/LU
            "Latvia",                               // Code LVA/LV
            "Macau SAR China",                      // Code MAC/MO
            "St. Martin",                           // Code MAF/MF
            "Morocco",                              // Code MAR/MA
            "Monaco",                               // Code MCO/MC
            "Moldova",                              // Code MDA/MD
            "Madagascar",                           // Code MDG/MG
            "Maldives",                             // Code MDV/MV
            "Mexico",                               // Code MEX/MX
            "Marshall Islands",                     // Code MHL/MH
            "Macedonia",                            // Code MKD/MK
            "Mali",                                 // Code MLI/ML
            "Malta",                                // Code MLT/MT
            "Myanmar (Burma)",                      // Code MMR/MM
            "Montenegro",                           // Code MNE/ME
            "Mongolia",                             // Code MNG/MN
            "Northern Mariana Islands",             // Code MNP/MP
            "Mozambique",                           // Code MOZ/MZ
            "Mauritania",                           // Code MRT/MR
            "Montserrat",                           // Code MSR/MS
            "Martinique",                           // Code MTQ/MQ
            "Mauritius",                            // Code MUS/MU
            "Malawi",                               // Code MWI/MW
            "Malaysia",                             // Code MYS/MY
            "Mayotte",                              // Code MYT/YT
            "Namibia",                              // Code NAM/NA
            "New Caledonia",                        // Code NCL/NC
            "Niger",                                // Code NER/NE
            "Norfolk Island",                       // Code NFK/NF
            "Nigeria",                              // Code NGA/NG
            "Nicaragua",                            // Code NIC/NI
            "Niue",                                 // Code NIU/NU
            "Netherlands",                          // Code NLD/NL
            "Norway",                               // Code NOR/NO
            "Nepal",                                // Code NPL/NP
            "Nauru",                                // Code NRU/NR
            "New Zealand",                          // Code NZL/NZ
            "Oman",                                 // Code OMN/OM
            "Pakistan",                             // Code PAK/PK
            "Panama",                               // Code PAN/PA
            "Pitcairn Islands",                     // Code PCN/PN
            "Peru",                                 // Code PER/PE
            "Philippines",                          // Code PHL/PH
            "Palau",                                // Code PLW/PW
            "Papua New Guinea",                     // Code PNG/PG
            "Poland",                               // Code POL/PL
            "Puerto Rico",                          // Code PRI/PR
            "North Korea",                          // Code PRK/KP
            "Portugal",                             // Code PRT/PT
            "Paraguay",                             // Code PRY/PY
            "Palestine",                            // Code PSE/PS
            "French Polynesia",                     // Code PYF/PF
            "Qatar",                                // Code QAT/QA
            "R\u00e9union",                         // Code REU/RE
            "Romania",                              // Code ROU/RO
            "Russia",                               // Code RUS/RU
            "Rwanda",                               // Code RWA/RW
            "Saudi Arabia",                         // Code SAU/SA
            "Sudan",                                // Code SDN/SD
            "Senegal",                              // Code SEN/SN
            "Singapore",                            // Code SGP/SG
            "South Georgia and the South Sandwich Islands",// Code SGS/GS
            "St. Helena",                           // Code SHN/SH
            "Svalbard & Jan Mayen",                 // Code SJM/SJ
            "Solomon Islands",                      // Code SLB/SB
            "Sierra Leone",                         // Code SLE/SL
            "El Salvador",                          // Code SLV/SV
            "San Marino",                           // Code SMR/SM
            "Somalia",                              // Code SOM/SO
            "St. Pierre & Miquelon",                // Code SPM/PM
            "Serbia",                               // Code SRB/RS
            "South Sudan",                          // Code SSD/SS
            "S\u00e3o Tom\u00e9 & Pr\u00edncipe",   // Code STP/ST
            "Suriname",                             // Code SUR/SR
            "Slovakia",                             // Code SVK/SK
            "Slovenia",                             // Code SVN/SI
            "Sweden",                               // Code SWE/SE
            "Swaziland",                            // Code SWZ/SZ
            "Sint Maarten",                         // Code SXM/SX
            "Seychelles",                           // Code SYC/SC
            "Syria",                                // Code SYR/SY
            "Turks & Caicos Islands",               // Code TCA/TC
            "Chad",                                 // Code TCD/TD
            "Togo",                                 // Code TGO/TG
            "Thailand",                             // Code THA/TH
            "Tajikistan",                           // Code TJK/TJ
            "Tokelau",                              // Code TKL/TK
            "Turkmenistan",                         // Code TKM/TM
            "Timor-Leste",                          // Code TLS/TL
            "Tonga",                                // Code TON/TO
            "Trinidad & Tobago",                    // Code TTO/TT
            "Tunisia",                              // Code TUN/TN
            "Turkey",                               // Code TUR/TR
            "Tuvalu",                               // Code TUV/TV
            "Taiwan",                               // Code TWN/TW
            "Tanzania",                             // Code TZA/TZ
            "Uganda",                               // Code UGA/UG
            "Ukraine",                              // Code UKR/UA
            "U.S. Outlying Islands",                // Code UMI/UM
            "Uruguay",                              // Code URY/UY
            "United States",                        // Code USA/US
            "Uzbekistan",                           // Code UZB/UZ
            "Holy See (Vatican City State)",        // Code VAT/VA
            "St. Vincent & Grenadines",             // Code VCT/VC
            "Venezuela",                            // Code VEN/VE
            "British Virgin Islands",               // Code VGB/VG
            "U.S. Virgin Islands",                  // Code VIR/VI
            "Vietnam",                              // Code VNM/VN
            "Vanuatu",                              // Code VUT/VU
            "Wallis & Futuna",                      // Code WLF/WF
            "Samoa",                                // Code WSM/WS
            "Yemen",                                // Code YEM/YE
            "South Africa",                         // Code ZAF/ZA
            "Zambia",                               // Code ZMB/ZM
            "Zimbabwe"                              // Code ZWE/ZW
    };

    private static final String[] ISO_3166_1_CODES = new String[] {
            "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", "AS",
            "AT", "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", "BF", "BG",
            "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT",
            "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", "CH", "CI",
            "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CW", "CX", "CY",
            "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "EH",
            "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "GA", "GB",
            "GD", "GE", "GF", "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ",
            "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT",
            "HU", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT",
            "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP",
            "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS",
            "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH",
            "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU",
            "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI",
            "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG",
            "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", "QA",
            "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG",
            "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST",
            "SV", "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK",
            "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", "UG",
            "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU",
            "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW"
    };

    // Each element corresponds to an ISO2 code, and contains the index
    // for the corresponding ISO3 code
    private static final int[] ISO_3166_1_TO_2 = new int[] {
            6,   7,   1,  13,   3,   5,   9,   2,  11,   8,  10,
            15,  14,   0,   4,  16,  26,  33,  22,  18,  21,  23,
            24,  17,  19,  27,  30,  34,  31,  20,  32,  25,  35,
            36,  37,  28,  29,  39,  40,  46,  38,  47,  41,  44,
            48,  42,  45,  43,  49,  52,  53,  51,  54,  55,  57,
            58,  59,  60,  62,  61,  63,  64,  65,  70,  66,  68,
            67,  69,  71,  72,  73,  74,  77,  76,  75,  78,  79,
            90,  80,  93,  81,  82,  83,  91,  86,  84,  85,  88,
            89, 195,  92,  94,  87,  95,  96,  97,  98,  99, 100,
            101, 102, 106, 110, 103, 104, 105, 108, 107, 109, 111,
            113, 112, 114, 115, 117, 118, 119, 120,  50, 121, 181,
            122, 123,  56, 116, 124, 125, 128, 129, 130, 126, 131,
            132, 133, 134, 127, 137, 138, 139, 148, 136, 140, 143,
            144, 145, 147, 149, 135, 150, 154, 152, 153, 146, 155,
            141, 156, 142, 157, 151, 159, 160, 161, 162, 163, 164,
            166, 167, 168, 169, 165, 170, 171, 173, 175, 185, 178,
            176, 172, 179, 203, 174, 180, 184, 182, 177, 183, 186,
            187, 188, 204, 189, 190, 191, 198, 213, 192, 210, 194,
            196, 209, 197, 208, 199, 201, 193, 202, 207, 205, 206,
            200, 212, 214, 211, 215, 216,  12, 217, 218, 219, 220,
            222, 221, 225, 223, 226, 224, 227, 228, 229, 231, 230,
            232, 234, 233, 235, 236, 237, 238, 239, 240, 241, 242,
            243, 244, 245, 158, 246, 247, 248
    };

    // Each element corresponds to an ISO3 code, and contains the index
    // for the corresponding ISO2 code, or -1 if not represented
    private static final int[] ISO_3166_2_TO_1 = new int[] {
            13,   2,   7,   4,  14,   5,   0,   1,   9,   6,  10,
            8, 215,   3,  12,  11,  15,  23,  19,  24,  29,  20,
            18,  21,  22,  31,  16,  25,  35,  36,  26,  28,  30,
            17,  27,  32,  33,  34,  40,  37,  38,  42,  45,  47,
            43,  46,  39,  41,  44,  48, 118,  51,  49,  50,  52,
            53, 123,  54,  55,  56,  57,  59,  58,  60,  61,  62,
            64,  66,  65,  67,  63,  68,  69,  70,  71,  74,  73,
            72,  75,  76,  78,  80,  81,  82,  85,  86,  84,  92,
            87,  88,  77,  83,  90,  79,  91,  93,  94,  95,  96,
            97,  98,  99, 100, 103, 104, 105, 101, 107, 106, 108,
            102, 109, 111, 110, 112, 113, 124, 114, 115, 116, 117,
            119, 121, 122, 125, 126, 130, 135, 127, 128, 129, 131,
            132, 133, 134, 147, 140, 136, 137, 138, 141, 154, 156,
            142, 143, 144, 152, 145, 139, 146, 148, 158, 150, 151,
            149, 153, 155, 157, 245, 159, 160, 161, 162, 163, 164,
            169, 165, 166, 167, 168, 170, 171, 177, 172, 180, 173,
            176, 184, 175, 178, 181, 120, 183, 185, 182, 174, 186,
            187, 188, 190, 191, 192, 195, 204, 197,  89, 198, 200,
            193, 202, 209, 203, 205, 179, 189, 207, 208, 206, 201,
            199, 196, 212, 210, 194, 211, 213, 214, 216, 217, 218,
            219, 221, 220, 223, 225, 222, 224, 226, 227, 228, 230,
            229, 231, 233, 232, 234, 235, 236, 237, 238, 239, 240,
            241, 242, 243, 244, 246, 247, 248
    };
    // Language afr: ZAF,NAM
    private static final int[] REGIONS_AFR = new int[] { 246,159 };
    // Language ara: ARE,BHR,COM,DJI,DZA,EGY,ERI,ESH,IRQ,ISR,JOR,KWT,LBN,LBY,MAR,MRT,OMN,PSE,QAT,SAU,SDN,SOM,SSD,SYR,TCD,TUN,YEM
    private static final int[] REGIONS_ARA = new int[] { 7,24,50,60,64,66,67,68,108,110,114,123,125,127,137,152,171,184,186,191,192,202,205,214,216,225,245 };
    // Language ben: BGD,IND
    private static final int[] REGIONS_BEN = new int[] { 22,104 };
    // Language bod: CHN,IND
    private static final int[] REGIONS_BOD = new int[] { 43,104 };
    // Language cat: AND,ESP,FRA,ITA
    private static final int[] REGIONS_CAT = new int[] { 6,69,75,111 };
    // Language dan: DNK,GRL
    private static final int[] REGIONS_DAN = new int[] { 62,91 };
    // Language deu: DEU,AUT,BEL,CHE,LIE,LUX
    private static final int[] REGIONS_DEU = new int[] { 59,15,18,41,129,133 };
    // Language ell: GRC,CYP
    private static final int[] REGIONS_ELL = new int[] { 89,57 };
    // Language eng: USA,AIA,ASM,ATG,AUS,BEL,BHS,BLZ,BMU,BRB,BWA,CAN,CCK,CMR,COK,CXR,CYM,DMA,ERI,FJI,FLK,FSM,GBR,GGY,GHA,GIB,GMB,GRD,GUM,GUY,HKG,IMN,IND,IOT,IRL,JAM,JEY,KEN,KIR,KNA,LBR,LCA,LSO,MAC,MDG,MHL,MLT,MNP,MSR,MUS,MWI,MYS,NAM,NFK,NGA,NIU,NRU,NZL,PAK,PCN,PHL,PLW,PNG,PRI,RWA,SDN,SGP,SHN,SLB,SLE,SSD,SWZ,SXM,SYC,TCA,TKL,TON,TTO,TUV,TZA,UGA,UMI,VCT,VGB,VIR,VUT,WSM,ZAF,ZMB,ZWE
    private static final int[] REGIONS_ENG = new int[] { 234,3,10,13,14,18,25,29,30,33,37,39,40,45,48,55,56,61,67,73,74,77,79,81,82,83,86,90,94,95,96,103,104,105,106,112,113,117,120,121,126,128,131,135,140,143,146,150,153,155,156,157,159,162,163,165,169,170,172,174,176,177,178,180,190,192,194,196,198,199,205,211,212,213,215,220,223,224,227,229,230,232,237,239,240,242,244,246,247,248 };
    // Language ewe: GHA,TGO
    private static final int[] REGIONS_EWE = new int[] { 82,217 };
    // Language fas: IRN,AFG
    private static final int[] REGIONS_FAS = new int[] { 107,1 };
    // Language fra: FRA,BDI,BEL,BEN,BFA,BLM,CAF,CAN,CHE,CIV,CMR,COD,COG,COM,DJI,DZA,GAB,GIN,GLP,GNQ,GUF,HTI,LUX,MAF,MAR,MCO,MDG,MLI,MRT,MTQ,MUS,MYT,NCL,NER,PYF,REU,RWA,SEN,SPM,SYC,SYR,TCD,TGO,TUN,VUT,WLF
    private static final int[] REGIONS_FRA = new int[] { 75,17,18,19,21,27,38,39,41,44,45,46,47,50,60,64,78,84,85,88,93,100,133,136,137,138,140,145,152,154,155,158,160,161,185,187,190,193,203,213,214,216,217,225,242,243 };
    // Language ful: SEN,CMR,GIN,MRT
    private static final int[] REGIONS_FUL = new int[] { 193,45,84,152 };
    // Language gsw: CHE,FRA,LIE
    private static final int[] REGIONS_GSW = new int[] { 41,75,129 };
    // Language hau: NGA,GHA,NER
    private static final int[] REGIONS_HAU = new int[] { 163,82,161 };
    // Language hrv: HRV,BIH
    private static final int[] REGIONS_HRV = new int[] { 99,26 };
    // Language ita: ITA,CHE,SMR
    private static final int[] REGIONS_ITA = new int[] { 111,41,201 };
    // Language kor: KOR,PRK
    private static final int[] REGIONS_KOR = new int[] { 122,181 };
    // Language lin: COD,AGO,CAF,COG
    private static final int[] REGIONS_LIN = new int[] { 46,2,38,47 };
    // Language mas: KEN,TZA
    private static final int[] REGIONS_MAS = new int[] { 117,229 };
    // Language msa: MYS,BRN,SGP
    private static final int[] REGIONS_MSA = new int[] { 157,34,194 };
    // Language nep: NPL,IND
    private static final int[] REGIONS_NEP = new int[] { 168,104 };
    // Language nld: NLD,ABW,BEL,BES,CUW,SUR,SXM
    private static final int[] REGIONS_NLD = new int[] { 166,0,18,20,54,207,212 };
    // Language nob: NOR,SJM
    private static final int[] REGIONS_NOB = new int[] { 167,197 };
    // Language orm: ETH,KEN
    private static final int[] REGIONS_ORM = new int[] { 71,117 };
    // Language oss: RUS,GEO
    private static final int[] REGIONS_OSS = new int[] { 189,80 };
    // Language pan: PAK,IND
    private static final int[] REGIONS_PAN = new int[] { 172,104 };
    // Language por: PRT,AGO,BRA,CPV,GNB,MAC,MOZ,STP,TLS
    private static final int[] REGIONS_POR = new int[] { 182,2,32,51,87,135,151,206,222 };
    // Language que: PER,BOL,ECU
    private static final int[] REGIONS_QUE = new int[] { 175,31,65 };
    // Language ron: ROU,MDA
    private static final int[] REGIONS_RON = new int[] { 188,139 };
    // Language rus: RUS,BLR,KAZ,KGZ,MDA,UKR
    private static final int[] REGIONS_RUS = new int[] { 189,28,116,118,139,231 };
    // Language sme: NOR,FIN,SWE
    private static final int[] REGIONS_SME = new int[] { 167,72,210 };
    // Language som: SOM,DJI,ETH,KEN
    private static final int[] REGIONS_SOM = new int[] { 202,60,71,117 };
    // Language spa: ESP,ARG,BOL,CHL,COL,CRI,CUB,DOM,ECU,GNQ,GTM,HND,MEX,NIC,PAN,PER,PHL,PRI,PRY,SLV,URY,USA,VEN
    private static final int[] REGIONS_SPA = new int[] { 69,8,31,42,49,52,53,63,65,88,92,98,142,164,173,175,176,180,183,200,233,234,238 };
    // Language sqi: ALB,MKD
    private static final int[] REGIONS_SQI = new int[] { 5,144 };
    // Language srp: SRB,BIH,MNE
    private static final int[] REGIONS_SRP = new int[] { 204,26,148 };
    // Language swa: TZA,KEN,UGA
    private static final int[] REGIONS_SWA = new int[] { 229,117,230 };
    // Language swe: SWE,ALA,FIN
    private static final int[] REGIONS_SWE = new int[] { 210,4,72 };
    // Language tam: IND,LKA,MYS,SGP
    private static final int[] REGIONS_TAM = new int[] { 104,130,157,194 };
    // Language teo: KEN,UGA
    private static final int[] REGIONS_TEO = new int[] { 117,230 };
    // Language tir: ERI,ETH
    private static final int[] REGIONS_TIR = new int[] { 67,71 };
    // Language tur: TUR,CYP
    private static final int[] REGIONS_TUR = new int[] { 226,57 };
    // Language urd: IND,PAK
    private static final int[] REGIONS_URD = new int[] { 104,172 };
    // Language uzb: UZB,AFG
    private static final int[] REGIONS_UZB = new int[] { 235,1 };
    // Language yor: NGA,BEN
    private static final int[] REGIONS_YOR = new int[] { 163,19 };
    // Language zho: CHN,HKG,MAC,SGP,TWN
    private static final int[] REGIONS_ZHO = new int[] { 43,96,135,194,228 };

    private static final int[][] LANGUAGE_REGIONS = new int[][] {
            null, null, null, null, null, null, null, null, REGIONS_AFR,
            null, null, null, null, null, null, null, null, null,
            null, null, REGIONS_ARA, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, REGIONS_BEN, null, null, null,
            null, null, null, null, null, null, REGIONS_BOD, null,
            null, null, null, null, null, null, null, null, null,
            null, null, REGIONS_CAT, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            REGIONS_DAN, null, null, null, null, null, REGIONS_DEU,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, REGIONS_ELL,
            null, REGIONS_ENG, null, null, null, null, REGIONS_EWE,
            null, null, null, REGIONS_FAS, null, null, null, null,
            null, null, REGIONS_FRA, null, null, null, null, null,
            REGIONS_FUL, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, REGIONS_GSW, null, null, null, null,
            null, REGIONS_HAU, null, null, null, null, null, null,
            null, null, null, REGIONS_HRV, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, REGIONS_ITA,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            REGIONS_KOR, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, REGIONS_LIN, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, REGIONS_MAS, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            REGIONS_MSA, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, REGIONS_NEP, null, null, null, null,
            REGIONS_NLD, null, null, null, REGIONS_NOB, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, REGIONS_ORM, null, REGIONS_OSS,
            null, null, null, null, null, null, REGIONS_PAN, null,
            null, null, null, null, null, null, null, REGIONS_POR,
            null, null, null, REGIONS_QUE, null, null, null, null,
            null, null, null, REGIONS_RON, null, null, REGIONS_RUS,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, REGIONS_SME, null, null, null, null,
            null, null, null, null, null, REGIONS_SOM, null, null,
            REGIONS_SPA, REGIONS_SQI, null, null, REGIONS_SRP, null,
            null, null, null, null, null, null, REGIONS_SWA, null,
            REGIONS_SWE, null, null, null, null, REGIONS_TAM, null,
            null, null, REGIONS_TEO, null, null, null, null, null,
            null, REGIONS_TIR, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, REGIONS_TUR,
            null, null, null, null, null, null, null, null, null,
            null, null, null, REGIONS_URD, REGIONS_UZB, null, null,
            null, null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null, null,
            null, REGIONS_YOR, null, null, null, null, null, null,
            REGIONS_ZHO, null, null, null, null
    };

    private static final int[] LANGUAGE_REGION = new int[] {
            71,  80,  -1,  -1,  -1,  -1,  -1,  -1,
            246,  45,  -1,  82,  -1,  -1,  -1,  -1,
            71,  -1,  -1,  -1,   7,  -1,  69,  -1,
            -1,  -1,  -1, 229, 104,  -1,  -1,  -1,
            16,  -1,  -1,  31,  16,  -1,  -1, 189,
            -1, 145,  -1,  45,  -1,  -1,  28, 247,
            22,  -1, 229,  -1, 104,  -1,  -1, 242,
            -1,  -1,  43,  26,  -1,  75, 104,  -1,
            -1,  -1,  23,  -1,  -1,  -1,  -1,   6,
            -1,  -1,  -1,  58, 230,  94,  -1, 189,
            -1,  -1,  -1,  -1,  -1,  -1, 234,  -1,
            189,  -1,  -1,  -1,  79,  75,  -1,  -1,
            -1,  39,  -1,  -1,  -1,  -1,  79,  -1,
            62,  -1, 117,  -1,  -1,  -1,  59,  -1,
            -1, 141, 161,  -1,  -1,  59,  45,  -1,
            193,  -1,  35, 117,  -1,  -1,  -1,  89,
            -1, 234,  -1,  -1,  70,  69,  82,  45,
            -1,  76, 107,  -1,  73, 176,  72,  -1,
            -1,  75,  -1,  -1,  -1,  -1, 166, 193,
            111,  -1,  -1,  -1,  -1,  -1,  -1,  79,
            106,  69, 103,  -1,  -1,  -1,  -1,  -1,
            -1,  -1, 183,  41, 104, 117,  -1,  -1,
            100, 163, 234, 110, 159,  -1,  -1, 104,
            -1,  -1, 178,  99,  59, 101,  -1,   9,
            -1, 163,  -1,  43,  -1,  39,  -1,  -1,
            -1,  -1, 102,  -1,  -1, 234,  -1,  -1,
            109, 111, 102,  -1,  45, 229, 115,  -1,
            -1,  -1,  64,  -1,  91, 117, 104,  -1,
            104,  80, 163,  -1, 116,  -1, 229,  51,
            -1,  -1, 119,  -1, 145, 117, 190, 118,
            45, 117,  -1, 104, 189,   2, 122,  -1,
            -1,  -1,  -1,  -1,  -1, 229,  45,  59,
            2,  -1,  -1,  -1,  -1, 229,  -1,  -1,
            124,  -1, 134,  -1, 166,  46, 132, 234,
            -1,  -1, 133,  -1,  46, 230,  -1,  -1,
            117,  -1, 117,  -1,  -1, 143,  -1,  -1,
            104,  -1, 170,  -1, 104, 117,  -1,  -1,
            -1, 117, 155,  -1, 151,  45,  -1,  -1,
            -1, 144,  -1, 140, 146,  -1,  -1,  -1,
            -1, 149,  -1, 157,  45,  -1,  -1,  -1,
            -1,  -1, 147,  -1,  -1,  -1,  -1,  -1,
            159, 169, 234, 246, 248, 159,  -1, 168,
            -1,  -1,  -1,  -1, 166,  45,  45, 167,
            167,  -1,  -1, 167,  -1,  -1,  -1, 192,
            -1, 156,  -1, 230,  -1,  -1,  75,  39,
            104,  71,  -1, 189,  -1,  -1,  -1,  -1,
            -1,  -1, 172,  -1,  -1,  -1,  -1,  -1,
            -1, 179,  -1, 182,  -1,  -1,   1, 175,
            -1,  -1,  -1,  -1, 229,  41,  -1, 188,
            17,  -1, 189, 229,  -1,  38, 189,  -1,
            -1,  -1, 104, 117,  -1,  -1, 229,  -1,
            -1, 151,  -1,  -1, 145,  -1,  -1, 137,
            -1,  -1, 130,  -1,  -1,  -1, 208, 209,
            -1, 167,  -1,  -1,  72, 244,  -1, 248,
            172,  -1,  -1, 202,  -1, 131,  69,   5,
            111,  -1, 204,  -1,  -1, 211,  -1, 102,
            -1,  -1, 229,  46, 210,  -1,  -1, 185,
            -1, 104, 189, 104,  -1, 117,  -1,  -1,
            219, 176, 218,  -1,  67,  -1,  -1,  -1,
            -1,  -1,  -1, 223,  -1,  -1,  37, 151,
            221,  -1,  -1, 226,  -1,  -1,  82, 161,
            -1, 137,  -1,  -1,  43, 231,  -1,  -1,
            104, 235, 126, 246, 241,  -1,  -1, 229,
            41,  -1,  -1,  -1,  -1,  -1,  18, 193,
            -1, 246, 230,  -1,  -1,  45, 234, 163,
            -1,  -1,  -1,  -1,  -1,  43,  43,  -1,
            246,  -1,  -1
    };

    static {
        // These maps should have been generated programmatically; look for accidental edits
        assert ISO_639_2_CODES.length == 531;
        assert ISO_639_2_NAMES.length == 531;
        assert ISO_639_2_TO_1.length == 531;
        assert ISO_639_1_CODES.length == 187;
        assert ISO_639_1_TO_2.length == 187;

        assert ISO_3166_2_CODES.length == 249;
        assert ISO_3166_2_NAMES.length == 249;
        assert ISO_3166_2_TO_1.length == 249;
        assert ISO_3166_1_CODES.length == 249;
        assert ISO_3166_1_TO_2.length == 249;

        assert LANGUAGE_REGION.length == 531;
        assert LANGUAGE_REGIONS.length == 531;
    }
}
