Source code

Revision control

Other Tools

// Generated by make_intl_data.py. DO NOT EDIT.
// Version: CLDR-37
#include "mozilla/Assertions.h"
#include "mozilla/Span.h"
#include "mozilla/TextUtils.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <string>
#include <type_traits>
#include "builtin/intl/LanguageTag.h"
#include "util/Text.h"
#include "vm/JSContext.h"
using namespace js::intl::LanguageTagLimits;
template <size_t Length, size_t TagLength, size_t SubtagLength>
static inline bool HasReplacement(
const char (&subtags)[Length][TagLength],
const js::intl::LanguageTagSubtag<SubtagLength>& subtag) {
MOZ_ASSERT(subtag.length() == TagLength - 1,
"subtag must have the same length as the list of subtags");
const char* ptr = subtag.span().data();
return std::binary_search(std::begin(subtags), std::end(subtags), ptr,
[](const char* a, const char* b) {
return memcmp(a, b, TagLength - 1) < 0;
});
}
template <size_t Length, size_t TagLength, size_t SubtagLength>
static inline const char* SearchReplacement(
const char (&subtags)[Length][TagLength],
const char* (&aliases)[Length],
const js::intl::LanguageTagSubtag<SubtagLength>& subtag) {
MOZ_ASSERT(subtag.length() == TagLength - 1,
"subtag must have the same length as the list of subtags");
const char* ptr = subtag.span().data();
auto p = std::lower_bound(std::begin(subtags), std::end(subtags), ptr,
[](const char* a, const char* b) {
return memcmp(a, b, TagLength - 1) < 0;
});
if (p != std::end(subtags) && memcmp(*p, ptr, TagLength - 1) == 0) {
return aliases[std::distance(std::begin(subtags), p)];
}
return nullptr;
}
#ifdef DEBUG
static bool IsAsciiLowercaseAlphanumeric(char c) {
return mozilla::IsAsciiLowercaseAlpha(c) || mozilla::IsAsciiDigit(c);
}
static bool IsAsciiLowercaseAlphanumericOrDash(char c) {
return IsAsciiLowercaseAlphanumeric(c) || c == '-';
}
static bool IsCanonicallyCasedLanguageTag(mozilla::Span<const char> span) {
// Tell the analysis the |std::all_of| function can't GC.
JS::AutoSuppressGCAnalysis nogc;
return std::all_of(span.begin(), span.end(), mozilla::IsAsciiLowercaseAlpha<char>);
}
static bool IsCanonicallyCasedRegionTag(mozilla::Span<const char> span) {
// Tell the analysis the |std::all_of| function can't GC.
JS::AutoSuppressGCAnalysis nogc;
return std::all_of(span.begin(), span.end(), mozilla::IsAsciiUppercaseAlpha<char>) ||
std::all_of(span.begin(), span.end(), mozilla::IsAsciiDigit<char>);
}
static bool IsCanonicallyCasedVariantTag(mozilla::Span<const char> span) {
// Tell the analysis the |std::all_of| function can't GC.
JS::AutoSuppressGCAnalysis nogc;
return std::all_of(span.begin(), span.end(), IsAsciiLowercaseAlphanumeric);
}
static bool IsCanonicallyCasedUnicodeKey(mozilla::Span<const char> key) {
return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric);
}
static bool IsCanonicallyCasedUnicodeType(mozilla::Span<const char> type) {
return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash);
}
static bool IsCanonicallyCasedTransformKey(mozilla::Span<const char> key) {
return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric);
}
static bool IsCanonicallyCasedTransformType(mozilla::Span<const char> type) {
return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash);
}
#endif
// Mappings from language subtags to preferred values.
// Derived from CLDR Supplemental Data, version 37.
bool js::intl::LanguageTag::languageMapping(LanguageSubtag& language) {
MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span()));
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span()));
if (language.length() == 2) {
static const char languages[9][3] = {
"bh", "in", "iw", "ji", "jw", "mo", "no", "tl", "tw",
};
static const char* aliases[9] = {
"bho", "id", "he", "yi", "jv", "ro", "nb", "fil", "ak",
};
if (const char* replacement = SearchReplacement(languages, aliases, language)) {
language.set(mozilla::MakeStringSpan(replacement));
return true;
}
return false;
}
if (language.length() == 3) {
static const char languages[345][4] = {
"aam", "aar", "abk", "adp", "afr", "aju", "aka", "alb", "als", "amh",
"ara", "arb", "arg", "arm", "asd", "asm", "aue", "ava", "ave", "aym",
"ayr", "ayx", "aze", "azj", "bak", "bam", "baq", "bcc", "bcl", "bel",
"ben", "bgm", "bih", "bis", "bjd", "bod", "bos", "bre", "bul", "bur",
"bxk", "bxr", "cat", "ccq", "ces", "cha", "che", "chi", "chu", "chv",
"cjr", "cka", "cld", "cmk", "cmn", "cor", "cos", "coy", "cqu", "cre",
"cwd", "cym", "cze", "dan", "deu", "dgo", "dhd", "dik", "diq", "dit",
"div", "drh", "dut", "dzo", "ekk", "ell", "emk", "eng", "epo", "esk",
"est", "eus", "ewe", "fao", "fas", "fat", "fij", "fin", "fra", "fre",
"fry", "fuc", "ful", "gav", "gaz", "gbo", "geo", "ger", "gfx", "ggn",
"gla", "gle", "glg", "glv", "gno", "gre", "grn", "gti", "gug", "guj",
"guv", "gya", "hat", "hau", "hdn", "hea", "heb", "her", "him", "hin",
"hmo", "hrr", "hrv", "hun", "hye", "ibi", "ibo", "ice", "ido", "iii",
"ike", "iku", "ile", "ilw", "ina", "ind", "ipk", "isl", "ita", "jav",
"jeg", "jpn", "kal", "kan", "kas", "kat", "kau", "kaz", "kgc", "kgh",
"khk", "khm", "kik", "kin", "kir", "kmr", "knc", "kng", "knn", "koj",
"kom", "kon", "kor", "kpv", "krm", "ktr", "kua", "kur", "kvs", "kwq",
"kxe", "kzj", "kzt", "lao", "lat", "lav", "lbk", "lii", "lim", "lin",
"lit", "llo", "lmm", "ltz", "lub", "lug", "lvs", "mac", "mah", "mal",
"mao", "mar", "may", "meg", "mhr", "mkd", "mlg", "mlt", "mnk", "mol",
"mon", "mri", "msa", "mst", "mup", "mwj", "mya", "myd", "myt", "nad",
"nau", "nav", "nbl", "ncp", "nde", "ndo", "nep", "nld", "nno", "nns",
"nnx", "nob", "nor", "npi", "nts", "nya", "oci", "ojg", "oji", "ori",
"orm", "ory", "oss", "oun", "pan", "pbu", "pcr", "per", "pes", "pli",
"plt", "pmc", "pmu", "pnb", "pol", "por", "ppa", "ppr", "pry", "pus",
"puz", "que", "quz", "rmy", "roh", "ron", "rum", "run", "rus", "sag",
"san", "sca", "scc", "scr", "sin", "skk", "slk", "slo", "slv", "sme",
"smo", "sna", "snd", "som", "sot", "spa", "spy", "sqi", "src", "srd",
"srp", "ssw", "sun", "swa", "swe", "swh", "tah", "tam", "tat", "tdu",
"tel", "tgk", "tgl", "tha", "thc", "thx", "tib", "tie", "tir", "tkk",
"tlw", "tmp", "tne", "ton", "tsf", "tsn", "tso", "ttq", "tuk", "tur",
"twi", "uig", "ukr", "umu", "uok", "urd", "uzb", "uzn", "ven", "vie",
"vol", "wel", "wln", "wol", "xba", "xho", "xia", "xkh", "xpe", "xsj",
"xsl", "ybd", "ydd", "yid", "yma", "ymt", "yor", "yos", "yuu", "zai",
"zha", "zho", "zsm", "zul", "zyb",
};
static const char* aliases[345] = {
"aas", "aa", "ab", "dz", "af", "jrb", "ak", "sq", "sq", "am",
"ar", "ar", "an", "hy", "snz", "as", "ktz", "av", "ae", "ay",
"ay", "nun", "az", "az", "ba", "bm", "eu", "bal", "bik", "be",
"bn", "bcg", "bho", "bi", "drl", "bo", "bs", "br", "bg", "my",
"luy", "bua", "ca", "rki", "cs", "ch", "ce", "zh", "cu", "cv",
"mom", "cmr", "syr", "xch", "zh", "kw", "co", "pij", "quh", "cr",
"cr", "cy", "cs", "da", "de", "doi", "mwr", "din", "zza", "dif",
"dv", "mn", "nl", "dz", "et", "el", "man", "en", "eo", "ik",
"et", "eu", "ee", "fo", "fa", "ak", "fj", "fi", "fr", "fr",
"fy", "ff", "ff", "dev", "om", "grb", "ka", "de", "vaj", "gvr",
"gd", "ga", "gl", "gv", "gon", "el", "gn", "nyc", "gn", "gu",
"duz", "gba", "ht", "ha", "hai", "hmn", "he", "hz", "srx", "hi",
"ho", "jal", "hr", "hu", "hy", "opa", "ig", "is", "io", "ii",
"iu", "iu", "ie", "gal", "ia", "id", "ik", "is", "it", "jv",
"oyb", "ja", "kl", "kn", "ks", "ka", "kr", "kk", "tdf", "kml",
"mn", "km", "ki", "rw", "ky", "ku", "kr", "kg", "kok", "kwv",
"kv", "kg", "ko", "kv", "bmf", "dtp", "kj", "ku", "gdj", "yam",
"tvd", "dtp", "dtp", "lo", "la", "lv", "bnc", "raq", "li", "ln",
"lt", "ngt", "rmx", "lb", "lu", "lg", "lv", "mk", "mh", "ml",
"mi", "mr", "ms", "cir", "chm", "mk", "mg", "mt", "man", "ro",
"mn", "mi", "ms", "mry", "raj", "vaj", "my", "aog", "mry", "xny",
"na", "nv", "nr", "kdz", "nd", "ng", "ne", "nl", "nn", "nbr",
"ngv", "nb", "nb", "ne", "pij", "ny", "oc", "oj", "oj", "or",
"om", "or", "os", "vaj", "pa", "ps", "adx", "fa", "fa", "pi",
"mg", "huw", "phr", "lah", "pl", "pt", "bfy", "lcq", "prt", "ps",
"pub", "qu", "qu", "rom", "rm", "ro", "ro", "rn", "ru", "sg",
"sa", "hle", "sr", "hr", "si", "oyb", "sk", "sk", "sl", "se",
"sm", "sn", "sd", "so", "st", "es", "kln", "sq", "sc", "sc",
"sr", "ss", "su", "sw", "sv", "sw", "ty", "ta", "tt", "dtp",
"te", "tg", "fil", "th", "tpo", "oyb", "bo", "ras", "ti", "twm",
"weo", "tyj", "kak", "to", "taj", "tn", "ts", "tmh", "tk", "tr",
"ak", "ug", "uk", "del", "ema", "ur", "uz", "uz", "ve", "vi",
"vo", "cy", "wa", "wo", "cax", "xh", "acn", "waw", "kpe", "suj",
"den", "rki", "yi", "yi", "lrr", "mtm", "yo", "zom", "yug", "zap",
"za", "zh", "ms", "zu", "za",
};
if (const char* replacement = SearchReplacement(languages, aliases, language)) {
language.set(mozilla::MakeStringSpan(replacement));
return true;
}
return false;
}
return false;
}
// Language subtags with complex mappings.
// Derived from CLDR Supplemental Data, version 37.
bool js::intl::LanguageTag::complexLanguageMapping(const LanguageSubtag& language) {
MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span()));
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span()));
if (language.length() == 2) {
return language.equalTo("sh");
}
if (language.length() == 3) {
static const char languages[6][4] = {
"cnr", "drw", "hbs", "prs", "swc", "tnf",
};
return HasReplacement(languages, language);
}
return false;
}
// Mappings from region subtags to preferred values.
// Derived from CLDR Supplemental Data, version 37.
bool js::intl::LanguageTag::regionMapping(RegionSubtag& region) {
MOZ_ASSERT(IsStructurallyValidRegionTag(region.span()));
MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span()));
if (region.length() == 2) {
static const char regions[23][3] = {
"BU", "CS", "CT", "DD", "DY", "FQ", "FX", "HV", "JT", "MI",
"NH", "NQ", "PU", "PZ", "QU", "RH", "TP", "UK", "VD", "WK",
"YD", "YU", "ZR",
};
static const char* aliases[23] = {
"MM", "RS", "KI", "DE", "BJ", "AQ", "FR", "BF", "UM", "UM",
"VU", "AQ", "UM", "PA", "EU", "ZW", "TL", "GB", "VN", "UM",
"YE", "RS", "CD",
};
if (const char* replacement = SearchReplacement(regions, aliases, region)) {
region.set(mozilla::MakeStringSpan(replacement));
return true;
}
return false;
}
{
static const char regions[300][4] = {
"004", "008", "010", "012", "016", "020", "024", "028", "031", "032",
"036", "040", "044", "048", "050", "051", "052", "056", "060", "062",
"064", "068", "070", "072", "074", "076", "084", "086", "090", "092",
"096", "100", "104", "108", "112", "116", "120", "124", "132", "136",
"140", "144", "148", "152", "156", "158", "162", "166", "170", "174",
"175", "178", "180", "184", "188", "191", "192", "196", "203", "204",
"208", "212", "214", "218", "222", "226", "230", "231", "232", "233",
"234", "238", "239", "242", "246", "248", "249", "250", "254", "258",
"260", "262", "266", "268", "270", "275", "276", "278", "280", "288",
"292", "296", "300", "304", "308", "312", "316", "320", "324", "328",
"332", "334", "336", "340", "344", "348", "352", "356", "360", "364",
"368", "372", "376", "380", "384", "388", "392", "398", "400", "404",
"408", "410", "414", "417", "418", "422", "426", "428", "430", "434",
"438", "440", "442", "446", "450", "454", "458", "462", "466", "470",
"474", "478", "480", "484", "492", "496", "498", "499", "500", "504",
"508", "512", "516", "520", "524", "528", "531", "533", "534", "535",
"540", "548", "554", "558", "562", "566", "570", "574", "578", "580",
"581", "583", "584", "585", "586", "591", "598", "600", "604", "608",
"612", "616", "620", "624", "626", "630", "634", "638", "642", "643",
"646", "652", "654", "659", "660", "662", "663", "666", "670", "674",
"678", "682", "686", "688", "690", "694", "702", "703", "704", "705",
"706", "710", "716", "720", "724", "728", "729", "732", "736", "740",
"744", "748", "752", "756", "760", "762", "764", "768", "772", "776",
"780", "784", "788", "792", "795", "796", "798", "800", "804", "807",
"818", "826", "830", "831", "832", "833", "834", "840", "850", "854",
"858", "860", "862", "876", "882", "886", "887", "891", "894", "958",
"959", "960", "962", "963", "964", "965", "966", "967", "968", "969",
"970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
"980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
"990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
};
static const char* aliases[300] = {
"AF", "AL", "AQ", "DZ", "AS", "AD", "AO", "AG", "AZ", "AR",
"AU", "AT", "BS", "BH", "BD", "AM", "BB", "BE", "BM", "034",
"BT", "BO", "BA", "BW", "BV", "BR", "BZ", "IO", "SB", "VG",
"BN", "BG", "MM", "BI", "BY", "KH", "CM", "CA", "CV", "KY",
"CF", "LK", "TD", "CL", "CN", "TW", "CX", "CC", "CO", "KM",
"YT", "CG", "CD", "CK", "CR", "HR", "CU", "CY", "CZ", "BJ",
"DK", "DM", "DO", "EC", "SV", "GQ", "ET", "ET", "ER", "EE",
"FO", "FK", "GS", "FJ", "FI", "AX", "FR", "FR", "GF", "PF",
"TF", "DJ", "GA", "GE", "GM", "PS", "DE", "DE", "DE", "GH",
"GI", "KI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GY",
"HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR",
"IQ", "IE", "IL", "IT", "CI", "JM", "JP", "KZ", "JO", "KE",
"KP", "KR", "KW", "KG", "LA", "LB", "LS", "LV", "LR", "LY",
"LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT",
"MQ", "MR", "MU", "MX", "MC", "MN", "MD", "ME", "MS", "MA",
"MZ", "OM", "NA", "NR", "NP", "NL", "CW", "AW", "SX", "BQ",
"NC", "VU", "NZ", "NI", "NE", "NG", "NU", "NF", "NO", "MP",
"UM", "FM", "MH", "PW", "PK", "PA", "PG", "PY", "PE", "PH",
"PN", "PL", "PT", "GW", "TL", "PR", "QA", "RE", "RO", "RU",
"RW", "BL", "SH", "KN", "AI", "LC", "MF", "PM", "VC", "SM",
"ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "VN", "SI",
"SO", "ZA", "ZW", "YE", "ES", "SS", "SD", "EH", "SD", "SR",
"SJ", "SZ", "SE", "CH", "SY", "TJ", "TH", "TG", "TK", "TO",
"TT", "AE", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "MK",
"EG", "GB", "JE", "GG", "JE", "IM", "TZ", "US", "VI", "BF",
"UY", "UZ", "VE", "WF", "WS", "YE", "YE", "RS", "ZM", "AA",
"QM", "QN", "QP", "QQ", "QR", "QS", "QT", "EU", "QV", "QW",
"QX", "QY", "QZ", "XA", "XB", "XC", "XD", "XE", "XF", "XG",
"XH", "XI", "XJ", "XK", "XL", "XM", "XN", "XO", "XP", "XQ",
"XR", "XS", "XT", "XU", "XV", "XW", "XX", "XY", "XZ", "ZZ",
};
if (const char* replacement = SearchReplacement(regions, aliases, region)) {
region.set(mozilla::MakeStringSpan(replacement));
return true;
}
return false;
}
}
// Region subtags with complex mappings.
// Derived from CLDR Supplemental Data, version 37.
bool js::intl::LanguageTag::complexRegionMapping(const RegionSubtag& region) {
MOZ_ASSERT(IsStructurallyValidRegionTag(region.span()));
MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span()));
if (region.length() == 2) {
return region.equalTo("AN") ||
region.equalTo("NT") ||
region.equalTo("PC") ||
region.equalTo("SU");
}
{
static const char regions[8][4] = {
"172", "200", "530", "532", "536", "582", "810", "890",
};
return HasReplacement(regions, region);
}
}
// Language subtags with complex mappings.
// Derived from CLDR Supplemental Data, version 37.
void js::intl::LanguageTag::performComplexLanguageMappings() {
MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span()));
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span()));
if (language().equalTo("cnr")) {
setLanguage("sr");
if (region().missing()) {
setRegion("ME");
}
}
else if (language().equalTo("drw") ||
language().equalTo("prs") ||
language().equalTo("tnf")) {
setLanguage("fa");
if (region().missing()) {
setRegion("AF");
}
}
else if (language().equalTo("hbs") ||
language().equalTo("sh")) {
setLanguage("sr");
if (script().missing()) {
setScript("Latn");
}
}
else if (language().equalTo("swc")) {
setLanguage("sw");
if (region().missing()) {
setRegion("CD");
}
}
}
// Region subtags with complex mappings.
// Derived from CLDR Supplemental Data, version 37.
void js::intl::LanguageTag::performComplexRegionMappings() {
MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span()));
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span()));
MOZ_ASSERT(IsStructurallyValidRegionTag(region().span()));
MOZ_ASSERT(IsCanonicallyCasedRegionTag(region().span()));
if (region().equalTo("172")) {
if (language().equalTo("hy") ||
(language().equalTo("und") && script().equalTo("Armn"))) {
setRegion("AM");
}
else if (language().equalTo("az") ||
language().equalTo("tkr") ||
language().equalTo("tly") ||
language().equalTo("ttt")) {
setRegion("AZ");
}
else if (language().equalTo("be")) {
setRegion("BY");
}
else if (language().equalTo("ab") ||
language().equalTo("ka") ||
(language().equalTo("ku") && script().equalTo("Yezi")) ||
language().equalTo("os") ||
(language().equalTo("und") && script().equalTo("Geor")) ||
(language().equalTo("und") && script().equalTo("Yezi")) ||
language().equalTo("xmf")) {
setRegion("GE");
}
else if (language().equalTo("ky")) {
setRegion("KG");
}
else if (language().equalTo("kk") ||
(language().equalTo("ug") && script().equalTo("Cyrl"))) {
setRegion("KZ");
}
else if (language().equalTo("gag")) {
setRegion("MD");
}
else if (language().equalTo("tg")) {
setRegion("TJ");
}
else if (language().equalTo("tk")) {
setRegion("TM");
}
else if (language().equalTo("crh") ||
language().equalTo("got") ||
language().equalTo("ji") ||
language().equalTo("rue") ||
language().equalTo("uk") ||
(language().equalTo("und") && script().equalTo("Goth"))) {
setRegion("UA");
}
else if (language().equalTo("kaa") ||
language().equalTo("sog") ||
(language().equalTo("und") && script().equalTo("Sogd")) ||
(language().equalTo("und") && script().equalTo("Chrs")) ||
(language().equalTo("und") && script().equalTo("Sogo")) ||
language().equalTo("uz") ||
language().equalTo("xco")) {
setRegion("UZ");
}
else {
setRegion("RU");
}
}
else if (region().equalTo("200")) {
if (language().equalTo("sk")) {
setRegion("SK");
}
else {
setRegion("CZ");
}
}
else if (region().equalTo("530") ||
region().equalTo("532") ||
region().equalTo("AN")) {
if (language().equalTo("vic")) {
setRegion("SX");
}
else {
setRegion("CW");
}
}
else if (region().equalTo("536") ||
region().equalTo("NT")) {
if (language().equalTo("akk") ||
language().equalTo("ckb") ||
(language().equalTo("ku") && script().equalTo("Arab")) ||
language().equalTo("mis") ||
language().equalTo("syr") ||
(language().equalTo("und") && script().equalTo("Xsux")) ||
(language().equalTo("und") && script().equalTo("Hatr")) ||
(language().equalTo("und") && script().equalTo("Syrc"))) {
setRegion("IQ");
}
else {
setRegion("SA");
}
}
else if (region().equalTo("582") ||
region().equalTo("PC")) {
if (language().equalTo("mh")) {
setRegion("MH");
}
else if (language().equalTo("pau")) {
setRegion("PW");
}
else {
setRegion("FM");
}
}
else if (region().equalTo("810") ||
region().equalTo("SU")) {
if (language().equalTo("hy") ||
(language().equalTo("und") && script().equalTo("Armn"))) {
setRegion("AM");
}
else if (language().equalTo("az") ||
language().equalTo("tkr") ||
language().equalTo("tly") ||
language().equalTo("ttt")) {
setRegion("AZ");
}
else if (language().equalTo("be")) {
setRegion("BY");
}
else if (language().equalTo("et") ||
language().equalTo("vro")) {
setRegion("EE");
}
else if (language().equalTo("ab") ||
language().equalTo("ka") ||
(language().equalTo("ku") && script().equalTo("Yezi")) ||
language().equalTo("os") ||
(language().equalTo("und") && script().equalTo("Geor")) ||
(language().equalTo("und") && script().equalTo("Yezi")) ||
language().equalTo("xmf")) {
setRegion("GE");
}
else if (language().equalTo("ky")) {
setRegion("KG");
}
else if (language().equalTo("kk") ||
(language().equalTo("ug") && script().equalTo("Cyrl"))) {
setRegion("KZ");
}
else if (language().equalTo("lt") ||
language().equalTo("sgs")) {
setRegion("LT");
}
else if (language().equalTo("ltg") ||
language().equalTo("lv")) {
setRegion("LV");
}
else if (language().equalTo("gag")) {
setRegion("MD");
}
else if (language().equalTo("tg")) {
setRegion("TJ");
}
else if (language().equalTo("tk")) {
setRegion("TM");
}
else if (language().equalTo("crh") ||
language().equalTo("got") ||
language().equalTo("ji") ||
language().equalTo("rue") ||
language().equalTo("uk") ||
(language().equalTo("und") && script().equalTo("Goth"))) {
setRegion("UA");
}
else if (language().equalTo("kaa") ||
language().equalTo("sog") ||
(language().equalTo("und") && script().equalTo("Sogd")) ||
(language().equalTo("und") && script().equalTo("Chrs")) ||
(language().equalTo("und") && script().equalTo("Sogo")) ||
language().equalTo("uz") ||
language().equalTo("xco")) {
setRegion("UZ");
}
else {
setRegion("RU");
}
}
else if (region().equalTo("890")) {
if (language().equalTo("bs")) {
setRegion("BA");
}
else if (language().equalTo("hr")) {
setRegion("HR");
}
else if (language().equalTo("mk")) {
setRegion("MK");
}
else if (language().equalTo("sl")) {
setRegion("SI");
}
else {
setRegion("RS");
}
}
}
static const char* ToCharPointer(const char* str) {
return str;
}
static const char* ToCharPointer(const js::UniqueChars& str) {
return str.get();
}
template <typename T, typename U = T>
static bool IsLessThan(const T& a, const U& b) {
return strcmp(ToCharPointer(a), ToCharPointer(b)) < 0;
}
// Mappings from variant subtags to preferred values.
// Derived from CLDR Supplemental Data, version 37.
bool js::intl::LanguageTag::performVariantMappings(JSContext* cx) {
// The variant subtags need to be sorted for binary search.
MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(),
IsLessThan<decltype(variants_)::ElementType>));
auto insertVariantSortedIfNotPresent = [&](const char* variant) {
auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant,
IsLessThan<decltype(variants_)::ElementType,
decltype(variant)>);
// Don't insert the replacement when already present.
if (p != variants_.end() && strcmp(p->get(), variant) == 0) {
return true;
}
// Insert the preferred variant in sort order.
auto preferred = DuplicateString(cx, variant);
if (!preferred) {
return false;
}
return !!variants_.insert(p, std::move(preferred));
};
for (size_t i = 0; i < variants_.length(); ) {
auto& variant = variants_[i];
MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant.get())));
if (strcmp(variant.get(), "aaland") == 0) {
variants_.erase(variants_.begin() + i);
setRegion("AX");
}
else if (strcmp(variant.get(), "arevela") == 0) {
variants_.erase(variants_.begin() + i);
setLanguage("hy");
}
else if (strcmp(variant.get(), "arevmda") == 0) {
variants_.erase(variants_.begin() + i);
setLanguage("hyw");
}
else if (strcmp(variant.get(), "heploc") == 0) {
variants_.erase(variants_.begin() + i);
if (!insertVariantSortedIfNotPresent("alalc97")) {
return false;
}
}
else if (strcmp(variant.get(), "polytoni") == 0) {
variants_.erase(variants_.begin() + i);
if (!insertVariantSortedIfNotPresent("polyton")) {
return false;
}
}
else {
i++;
}
}
return true;
}
// Canonicalize grandfathered locale identifiers.
// Derived from CLDR Supplemental Data, version 37.
bool js::intl::LanguageTag::updateGrandfatheredMappings(JSContext* cx) {
// We're mapping regular grandfathered tags to non-grandfathered form here.
// Other tags remain unchanged.
//
// regular = "art-lojban"
// / "cel-gaulish"
// / "no-bok"
// / "no-nyn"
// / "zh-guoyu"
// / "zh-hakka"
// / "zh-min"
// / "zh-min-nan"
// / "zh-xiang"
//
// Therefore we can quickly exclude most tags by checking every
// |unicode_locale_id| subcomponent for characteristics not shared by any of
// the regular grandfathered (RG) tags:
//
// * Real-world |unicode_language_subtag|s are all two or three letters,
// so don't waste time running a useless |language.length > 3| fast-path.
// * No RG tag has a "script"-looking component.
// * No RG tag has a "region"-looking component.
// * The RG tags that match |unicode_locale_id| (art-lojban, cel-gaulish,
// zh-guoyu, zh-hakka, zh-xiang) have exactly one "variant". (no-bok,
// no-nyn, zh-min, and zh-min-nan require BCP47's extlang subtag
// that |unicode_locale_id| doesn't support.)
// * No RG tag contains |extensions| or |pu_extensions|.
if (script().present() ||
region().present() ||
variants().length() != 1 ||
extensions().length() != 0 ||
privateuse()) {
return true;
}
MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span()));
MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variants()[0].get())));
auto variantEqualTo = [this](const char* variant) {
return strcmp(variants()[0].get(), variant) == 0;
};
// art-lojban -> jbo
if (language().equalTo("art") && variantEqualTo("lojban")) {
setLanguage("jbo");
clearVariants();
return true;
}
// cel-gaulish -> xtg-x-cel-gaulish
else if (language().equalTo("cel") && variantEqualTo("gaulish")) {
setLanguage("xtg");
clearVariants();
auto privateuse = DuplicateString(cx, "x-cel-gaulish");
if (!privateuse) {
return false;
}
setPrivateuse(std::move(privateuse));
return true;
}
// zh-guoyu -> zh
else if (language().equalTo("zh") && variantEqualTo("guoyu")) {
setLanguage("zh");
clearVariants();
return true;
}
// zh-hakka -> hak
else if (language().equalTo("zh") && variantEqualTo("hakka")) {
setLanguage("hak");
clearVariants();
return true;
}
// zh-xiang -> hsn
else if (language().equalTo("zh") && variantEqualTo("xiang")) {
setLanguage("hsn");
clearVariants();
return true;
}
return true;
}
template <size_t Length>
static inline bool IsUnicodeKey(
mozilla::Span<const char> key, const char (&str)[Length]) {
static_assert(Length == UnicodeKeyLength + 1,
"Unicode extension key is two characters long");
return memcmp(key.data(), str, Length - 1) == 0;
}
template <size_t Length>
static inline bool IsUnicodeType(
mozilla::Span<const char> type, const char (&str)[Length]) {
static_assert(Length > UnicodeKeyLength + 1,
"Unicode extension type contains more than two characters");
return type.size() == (Length - 1) &&
memcmp(type.data(), str, Length - 1) == 0;
}
static int32_t CompareUnicodeType(const char* a, mozilla::Span<const char> b) {
MOZ_ASSERT(!std::char_traits<char>::find(b.data(), b.size(), '\0'),
"unexpected null-character in string");
using UnsignedChar = unsigned char;
for (size_t i = 0; i < b.size(); i++) {
// |a| is zero-terminated and |b| doesn't contain a null-terminator. So if
// we've reached the end of |a|, the below if-statement will always be true.
// That ensures we don't read past the end of |a|.
if (int32_t r = UnsignedChar(a[i]) - UnsignedChar(b[i])) {
return r;
}
}
// Return zero if both strings are equal or a negative number if |b| is a
// prefix of |a|.
return -int32_t(UnsignedChar(a[b.size()]));
}
template <size_t Length>
static inline const char* SearchUnicodeReplacement(
const char* (&types)[Length], const char* (&aliases)[Length],
mozilla::Span<const char> type) {
auto p = std::lower_bound(std::begin(types), std::end(types), type,
[](const auto& a, const auto& b) {
return CompareUnicodeType(a, b) < 0;
});
if (p != std::end(types) && CompareUnicodeType(*p, type) == 0) {
return aliases[std::distance(std::begin(types), p)];
}
return nullptr;
}
/**
* Mapping from deprecated BCP 47 Unicode extension types to their preferred
* values.
*
*/
const char* js::intl::LanguageTag::replaceUnicodeExtensionType(
mozilla::Span<const char> key, mozilla::Span<const char> type) {
MOZ_ASSERT(key.size() == UnicodeKeyLength);
MOZ_ASSERT(IsCanonicallyCasedUnicodeKey(key));
MOZ_ASSERT(type.size() > UnicodeKeyLength);
MOZ_ASSERT(IsCanonicallyCasedUnicodeType(type));
if (IsUnicodeKey(key, "ca")) {
if (IsUnicodeType(type, "ethiopic-amete-alem")) {
return "ethioaa";
}
if (IsUnicodeType(type, "islamicc")) {
return "islamic-civil";
}
}
else if (IsUnicodeKey(key, "kb") ||
IsUnicodeKey(key, "kc") ||
IsUnicodeKey(key, "kh") ||
IsUnicodeKey(key, "kk") ||
IsUnicodeKey(key, "kn")) {
if (IsUnicodeType(type, "yes")) {
return "true";
}
}
else if (IsUnicodeKey(key, "ks")) {
if (IsUnicodeType(type, "primary")) {
return "level1";
}
if (IsUnicodeType(type, "tertiary")) {
return "level3";
}
}
else if (IsUnicodeKey(key, "ms")) {
if (IsUnicodeType(type, "imperial")) {
return "uksystem";
}
}
else if (IsUnicodeKey(key, "rg") ||
IsUnicodeKey(key, "sd")) {
static const char* types[117] = {
"cn11", "cn12", "cn13", "cn14", "cn15", "cn21", "cn22", "cn23",
"cn31", "cn32", "cn33", "cn34", "cn35", "cn36", "cn37", "cn41",
"cn42", "cn43", "cn44", "cn45", "cn46", "cn50", "cn51", "cn52",
"cn53", "cn54", "cn61", "cn62", "cn63", "cn64", "cn65", "cz10a",
"cz10b", "cz10c", "cz10d", "cz10e", "cz10f", "cz611", "cz612", "cz613",
"cz614", "cz615", "cz621", "cz622", "cz623", "cz624", "cz626", "cz627",
"czjc", "czjm", "czka", "czkr", "czli", "czmo", "czol", "czpa",
"czpl", "czpr", "czst", "czus", "czvy", "czzl", "fra", "frb",
"frc", "frd", "fre", "frf", "frg", "frh", "fri", "frj",
"frk", "frl", "frm", "frn", "fro", "frp", "frq", "frr",
"frs", "frt", "fru", "frv", "laxn", "lud", "lug", "lul",
"mrnkc", "no23", "nzn", "nzs", "omba", "omsh", "plds", "plkp",
"pllb", "plld", "pllu", "plma", "plmz", "plop", "plpd", "plpk",
"plpm", "plsk", "plsl", "plwn", "plwp", "plzp", "tteto", "ttrcm",
"ttwto", "twkhq", "twtnq", "twtpq", "twtxq",
};
static const char* aliases[117] = {
"cnbj", "cntj", "cnhe", "cnsx", "cnmn", "cnln", "cnjl", "cnhl",
"cnsh", "cnjs", "cnzj", "cnah", "cnfj", "cnjx", "cnsd", "cnha",
"cnhb", "cnhn", "cngd", "cngx", "cnhi", "cncq", "cnsc", "cngz",
"cnyn", "cnxz", "cnsn", "cngs", "cnqh", "cnnx", "cnxj", "cz110",
"cz111", "cz112", "cz113", "cz114", "cz115", "cz663", "cz632", "cz633",
"cz634", "cz635", "cz641", "cz642", "cz643", "cz644", "cz646", "cz647",
"cz31", "cz64", "cz41", "cz52", "cz51", "cz80", "cz71", "cz53",
"cz32", "cz10", "cz20", "cz42", "cz63", "cz72", "frges", "frnaq",
"frara", "frbfc", "frbre", "frcvl", "frges", "frcor", "frbfc", "fridf",
"frocc", "frnaq", "frges", "frocc", "frhdf", "frnor", "frnor", "frpdl",
"frhdf", "frnaq", "frpac", "frara", "laxs", "lucl", "luec", "luca",
"mr13", "no50", "nzauk", "nzcan", "ombj", "omsj", "pl02", "pl04",
"pl08", "pl10", "pl06", "pl12", "pl14", "pl16", "pl20", "pl18",
"pl22", "pl26", "pl24", "pl28", "pl30", "pl32", "tttob", "ttmrc",
"tttob", "twkhh", "twtnn", "twnwt", "twtxg",
};
return SearchUnicodeReplacement(types, aliases, type);
}
else if (IsUnicodeKey(key, "tz")) {
static const char* types[28] = {
"aqams", "cnckg", "cnhrb", "cnkhg", "cuba", "egypt",
"eire", "est", "gmt0", "hongkong", "hst", "iceland",
"iran", "israel", "jamaica", "japan", "libya", "mst",
"navajo", "poland", "portugal", "prc", "roc", "rok",
"turkey", "uct", "usnavajo", "zulu",
};
static const char* aliases[28] = {
"nzakl", "cnsha", "cnsha", "cnurc", "cuhav", "egcai",
"iedub", "utcw05", "gmt", "hkhkg", "utcw10", "isrey",
"irthr", "jeruslm", "jmkin", "jptyo", "lytip", "utcw07",
"usden", "plwaw", "ptlis", "cnsha", "twtpe", "krsel",
"trist", "utc", "usden", "utc",
};
return SearchUnicodeReplacement(types, aliases, type);
}
return nullptr;
}
template <size_t Length>
static inline bool IsTransformKey(
mozilla::Span<const char> key, const char (&str)[Length]) {
static_assert(Length == TransformKeyLength + 1,
"Transform extension key is two characters long");
return memcmp(key.data(), str, Length - 1) == 0;
}
template <size_t Length>
static inline bool IsTransformType(
mozilla::Span<const char> type, const char (&str)[Length]) {
static_assert(Length > TransformKeyLength + 1,
"Transform extension type contains more than two characters");
return type.size() == (Length - 1) &&
memcmp(type.data(), str, Length - 1) == 0;
}
/**
* Mapping from deprecated BCP 47 Transform extension types to their preferred
* values.
*
*/
const char* js::intl::LanguageTag::replaceTransformExtensionType(
mozilla::Span<const char> key, mozilla::Span<const char> type) {
MOZ_ASSERT(key.size() == TransformKeyLength);
MOZ_ASSERT(IsCanonicallyCasedTransformKey(key));
MOZ_ASSERT(type.size() > TransformKeyLength);
MOZ_ASSERT(IsCanonicallyCasedTransformType(type));
if (IsTransformKey(key, "d0")) {
if (IsTransformType(type, "name")) {
return "charname";
}
}
else if (IsTransformKey(key, "m0")) {
if (IsTransformType(type, "names")) {
return "prprname";
}
}
return nullptr;
}