diff options
author | Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> | 2021-11-22 15:56:53 +0100 |
---|---|---|
committer | Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> | 2021-12-09 03:45:08 +0100 |
commit | 4f53c703e40bea3203259c212c54dc4816c08b09 (patch) | |
tree | 178ce98f0707d0e6b509cc603f0f92580ecd5775 | |
parent | 0fbeac01156c57dc6e48087b7a8dea4644294f6a (diff) |
QLocale: Extend support for language codes
This commit extends functionality for QLocale::codeToLanguage()
and QLocale::languageToCode() by adding an additional argument
that allows selection of the ISO 639 code-set to consider for
those operations.
The following ISO 639 codes are supported:
* Part 1
* Part 2 bibliographic
* Part 2 terminological
* Part 3
As a result of this change the codeToLanguage() overload without
the additional argument now returns a Language value if it matches
any know code. Previously a valid language was returned only if
the function argument matched the first code defined for that
language from the above list.
[ChangeLog][QtCore][QLocale] Added overloads for codeToLanguage()
and languageToCode() that support specifying which ISO 639 codes
to consider.
Fixes: QTBUG-98129
Change-Id: I4da8a89e2e68a673cf63a621359cded609873fa2
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r-- | src/corelib/text/qlocale.cpp | 136 | ||||
-rw-r--r-- | src/corelib/text/qlocale.h | 25 | ||||
-rw-r--r-- | src/corelib/text/qlocale.qdoc | 19 | ||||
-rw-r--r-- | src/corelib/text/qlocale_data_p.h | 687 | ||||
-rw-r--r-- | src/corelib/text/qlocale_mac.mm | 8 | ||||
-rw-r--r-- | src/corelib/text/qlocale_p.h | 15 | ||||
-rw-r--r-- | tests/auto/corelib/text/qlocale/tst_qlocale.cpp | 38 | ||||
-rw-r--r-- | util/locale_database/iso639_3.py | 105 | ||||
-rwxr-xr-x | util/locale_database/qlocalexml2cpp.py | 53 |
9 files changed, 724 insertions, 362 deletions
diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 45794cc703..660271230a 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -102,22 +102,58 @@ QT_BEGIN_INCLUDE_NAMESPACE #include "qlocale_data_p.h" QT_END_INCLUDE_NAMESPACE -QLocale::Language QLocalePrivate::codeToLanguage(QStringView code) noexcept +QLocale::Language QLocalePrivate::codeToLanguage(QStringView code, + QLocale::LanguageCodeTypes codeTypes) noexcept { const auto len = code.size(); if (len != 2 && len != 3) return QLocale::AnyLanguage; - char16_t uc1 = code[0].toLower().unicode(); - char16_t uc2 = code[1].toLower().unicode(); - char16_t uc3 = len > 2 ? code[2].toLower().unicode() : 0; - const unsigned char *c = language_code_list; - for (; *c != 0; c += 3) { - if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2]) - return QLocale::Language((c - language_code_list)/3); + const char16_t uc1 = code[0].toLower().unicode(); + const char16_t uc2 = code[1].toLower().unicode(); + const char16_t uc3 = len > 2 ? code[2].toLower().unicode() : 0; + + // All language codes are ASCII. + if (uc1 > 0x7F || uc2 > 0x7F || uc3 > 0x7F) + return QLocale::AnyLanguage; + + const AlphaCode codeBuf = { { char(uc1), char(uc2), char(uc3) } }; + + auto searchCode = [codeBuf](auto f) { + return std::find_if(languageCodeList.begin(), languageCodeList.end(), + [=](const LanguageCodeEntry &i) { return f(i) == codeBuf; }); + }; + + if (codeTypes.testFlag(QLocale::ISO639Part1) && uc3 == 0) { + auto i = searchCode([](const LanguageCodeEntry &i) { return i.part1; }); + if (i != languageCodeList.end()) + return QLocale::Language(std::distance(languageCodeList.begin(), i)); } - if (uc3 == 0) { + if (uc3 != 0) { + if (codeTypes.testFlag(QLocale::ISO639Part2B)) { + auto i = searchCode([](const LanguageCodeEntry &i) { return i.part2B; }); + if (i != languageCodeList.end()) + return QLocale::Language(std::distance(languageCodeList.begin(), i)); + } + + // Optimization: Part 2T code if present is always the same as Part 3 code. + // This is asserted in iso639_3.LanguageCodeData. + if (codeTypes.testFlag(QLocale::ISO639Part2T) + && !codeTypes.testFlag(QLocale::ISO639Part3)) { + auto i = searchCode([](const LanguageCodeEntry &i) { return i.part2T; }); + if (i != languageCodeList.end()) + return QLocale::Language(std::distance(languageCodeList.begin(), i)); + } + + if (codeTypes.testFlag(QLocale::ISO639Part3)) { + auto i = searchCode([](const LanguageCodeEntry &i) { return i.part3; }); + if (i != languageCodeList.end()) + return QLocale::Language(std::distance(languageCodeList.begin(), i)); + } + } + + if (codeTypes.testFlag(QLocale::LegacyLanguageCode) && uc3 == 0) { // legacy codes if (uc1 == 'n' && uc2 == 'o') // no -> nb return QLocale::NorwegianBokmal; @@ -177,15 +213,29 @@ QLocale::Territory QLocalePrivate::codeToTerritory(QStringView code) noexcept return QLocale::AnyTerritory; } -QLatin1String QLocalePrivate::languageToCode(QLocale::Language language) +QLatin1String QLocalePrivate::languageToCode(QLocale::Language language, + QLocale::LanguageCodeTypes codeTypes) { if (language == QLocale::AnyLanguage || language > QLocale::LastLanguage) return QLatin1String(); if (language == QLocale::C) return QLatin1String("C"); - const unsigned char *c = language_code_list + 3 * language; - return QLatin1String(reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3); + const LanguageCodeEntry &i = languageCodeList[language]; + + if (codeTypes.testFlag(QLocale::ISO639Part1) && i.part1.isValid()) + return QLatin1String(i.part1.code, 2); + + if (codeTypes.testFlag(QLocale::ISO639Part2B) && i.part2B.isValid()) + return QLatin1String(i.part2B.code, 3); + + if (codeTypes.testFlag(QLocale::ISO639Part2T) && i.part2T.isValid()) + return QLatin1String(i.part2T.code, 3); + + if (codeTypes.testFlag(QLocale::ISO639Part3)) + return QLatin1String(i.part3.code, 3); + + return QLatin1String(); } QLatin1String QLocalePrivate::scriptToCode(QLocale::Script script) @@ -370,20 +420,32 @@ QByteArray QLocaleId::name(char separator) const if (language_id == QLocale::C) return QByteArrayLiteral("C"); - const unsigned char *lang = language_code_list + 3 * language_id; + const LanguageCodeEntry &language = languageCodeList[language_id]; + const char *lang; + qsizetype langLen; + + if (language.part1.isValid()) { + lang = language.part1.code; + langLen = 2; + } else { + lang = language.part2B.isValid() ? language.part2B.code : language.part3.code; + langLen = 3; + } + const unsigned char *script = (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr); const unsigned char *country = (territory_id != QLocale::AnyTerritory ? territory_code_list + 3 * territory_id : nullptr); - char len = (lang[2] != 0 ? 3 : 2) + (script ? 4 + 1 : 0) - + (country ? (country[2] != 0 ? 3 : 2) + 1 : 0); + qsizetype len = langLen + (script ? 4 + 1 : 0) + (country ? (country[2] != 0 ? 3 : 2) + 1 : 0); QByteArray name(len, Qt::Uninitialized); char *uc = name.data(); + *uc++ = lang[0]; *uc++ = lang[1]; - if (lang[2] != 0) + if (langLen > 2) *uc++ = lang[2]; + if (script) { *uc++ = separator; *uc++ = script[0]; @@ -1348,30 +1410,66 @@ QString QLocale::bcp47Name() const Returns the two- or three-letter language code for \a language, as defined in the ISO 639 standards. + If specified, \a codeTypes selects which set of codes to consider. The first + code from the set that is defined for \a language is returned. Otherwise, + all ISO-639 codes are considered. The codes are considered in the following + order: \c ISO639Part1, \c ISO639Part2B, \c ISO639Part2T, \c ISO639Part3. + \c LegacyLanguageCode is ignored by this function. + \note For \c{QLocale::C} the function returns \c{"C"}. For \c QLocale::AnyLanguage an empty string is returned. + If the language has no code in any selected code set, an empty string + is returned. - \since 6.1 + \since 6.3 \sa codeToLanguage(), language(), name(), bcp47Name(), territoryToCode(), scriptToCode() */ +QString QLocale::languageToCode(Language language, LanguageCodeTypes codeTypes) +{ + return QLocalePrivate::languageToCode(language, codeTypes); +} + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) +/*! + \overload + \since 6.1 +*/ QString QLocale::languageToCode(Language language) { return QLocalePrivate::languageToCode(language); } +#endif /*! Returns the QLocale::Language enum corresponding to the two- or three-letter \a languageCode, as defined in the ISO 639 standards. - If the code is invalid or not known QLocale::AnyLanguage is returned. + If specified, \a codeTypes selects which set of codes to consider for + conversion. By default all codes known to Qt are considered. The codes are + matched in the following order: \c ISO639Part1, \c ISO639Part2B, + \c ISO639Part2T, \c ISO639Part3, \c LegacyLanguageCode. - \since 6.1 + If the code is invalid or not known \c QLocale::AnyLanguage is returned. + + \since 6.3 \sa languageToCode(), codeToTerritory(), codeToScript() */ +QLocale::Language QLocale::codeToLanguage(QStringView languageCode, + LanguageCodeTypes codeTypes) noexcept +{ + return QLocalePrivate::codeToLanguage(languageCode, codeTypes); +} + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) +/*! + \overload + \since 6.1 +*/ QLocale::Language QLocale::codeToLanguage(QStringView languageCode) noexcept { return QLocalePrivate::codeToLanguage(languageCode); } +#endif /*! \since 6.2 diff --git a/src/corelib/text/qlocale.h b/src/corelib/text/qlocale.h index 82852524eb..0a583b0881 100644 --- a/src/corelib/text/qlocale.h +++ b/src/corelib/text/qlocale.h @@ -1088,8 +1088,32 @@ public: QStringList uiLanguages() const; + enum LanguageCodeType { + ISO639Part1 = 1 << 0, + ISO639Part2B = 1 << 1, + ISO639Part2T = 1 << 2, + ISO639Part3 = 1 << 3, + LegacyLanguageCode = 1 << 15, + + ISO639Part2 = ISO639Part2B | ISO639Part2T, + ISO639Alpha2 = ISO639Part1, + ISO639Alpha3 = ISO639Part2 | ISO639Part3, + ISO639 = ISO639Alpha2 | ISO639Alpha3, + + AnyLanguageCode = -1 + }; + Q_DECLARE_FLAGS(LanguageCodeTypes, LanguageCodeType) + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) static QString languageToCode(Language language); + static QString languageToCode(Language language, LanguageCodeTypes codeTypes); static Language codeToLanguage(QStringView languageCode) noexcept; + static Language codeToLanguage(QStringView languageCode, LanguageCodeTypes codeTypes) noexcept; +#else + static QString languageToCode(Language language, LanguageCodeTypes codeTypes = AnyLanguageCode); + static Language codeToLanguage(QStringView languageCode, + LanguageCodeTypes codeTypes = AnyLanguageCode) noexcept; +#endif static QString territoryToCode(Territory territory); static Territory codeToTerritory(QStringView territoryCode) noexcept; #if QT_DEPRECATED_SINCE(6, 6) @@ -1146,6 +1170,7 @@ private: }; Q_DECLARE_SHARED(QLocale) Q_DECLARE_OPERATORS_FOR_FLAGS(QLocale::NumberOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QLocale::LanguageCodeTypes) #ifndef QT_NO_DATASTREAM Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QLocale &); diff --git a/src/corelib/text/qlocale.qdoc b/src/corelib/text/qlocale.qdoc index dc9c4e632f..9ea24d1433 100644 --- a/src/corelib/text/qlocale.qdoc +++ b/src/corelib/text/qlocale.qdoc @@ -1033,6 +1033,25 @@ \since 4.4 */ +/*! + \enum QLocale::LanguageCodeType + + This enum defines language code types that can be used to restrict set + of language codes considered by \c codeToLanguage and \c languageToCode. + + \value ISO639Part1 ISO 639 Part 1 Alpha 2 code. + \value ISO639Part2B ISO 639 Part 2 bibliographic Alpha 3 code. + \value ISO639Part2T ISO 639 Part 2 terminological Alpha 3 code. + \value ISO639Part3 ISO 639 Part 3 Alpha 3 code. + \value LegacyLanguageCode Codes that are not part of the above set, but that + were supported by Qt in the past. This value can only be used by + codeToLanguage(). It is ignored when passed to languageToCode(). + \value ISO639Part2 Any ISO 639 Part 2 code. + \value ISO639Alpha2 Any ISO-639 2-letter code. + \value ISO639Alpha3 Any ISO-639 3-letter code. + \value ISO639 Any ISO 639 code. + \value AnyLanguageCode Specifies that any code can be used. +*/ /*! \fn bool QLocale::operator==(const QLocale &lhs, const QLocale &rhs) diff --git a/src/corelib/text/qlocale_data_p.h b/src/corelib/text/qlocale_data_p.h index 4bd4d256b4..b0ed889762 100644 --- a/src/corelib/text/qlocale_data_p.h +++ b/src/corelib/text/qlocale_data_p.h @@ -51,6 +51,8 @@ // We mean it. // +#include <array> +#include <QtCore/qendian.h> #include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -74,10 +76,29 @@ static const TerritoryLanguage ImperialMeasurementSystems[] = { static const int ImperialMeasurementSystemsCount = sizeof(ImperialMeasurementSystems)/sizeof(ImperialMeasurementSystems[0]); +/* + Storage for alpha codes with length of up to 4 allowing efficient comparison. +*/ +struct alignas(uint32_t) AlphaCode { + char code[4]; + + bool isValid() const noexcept { return asU32() != 0; } + bool operator==(AlphaCode other) const noexcept { return asU32() == other.asU32(); } +private: + uint32_t asU32() const noexcept { return qFromUnaligned<uint32_t>(code); } +}; + +struct LanguageCodeEntry { + AlphaCode part1; + AlphaCode part2B; + AlphaCode part2T; + AlphaCode part3; +}; + // GENERATED PART STARTS HERE /* - This part of the file was generated on 2021-11-09 from the + This part of the file was generated on 2021-12-03 from the Common Locale Data Repository v40 http://www.unicode.org/cldr/ @@ -5337,338 +5358,338 @@ static const quint16 territory_name_index[] = { 2824, // Zimbabwe }; -static const unsigned char language_code_list[] = -" \0" // AnyLanguage -" \0" // C -"ab\0" // Abkhazian -"aa\0" // Afar -"af\0" // Afrikaans -"agq" // Aghem -"ak\0" // Akan -"akk" // Akkadian -"bss" // Akoose -"sq\0" // Albanian -"ase" // American Sign Language -"am\0" // Amharic -"egy" // Ancient Egyptian -"grc" // Ancient Greek -"ar\0" // Arabic -"an\0" // Aragonese -"arc" // Aramaic -"hy\0" // Armenian -"as\0" // Assamese -"ast" // Asturian -"asa" // Asu -"cch" // Atsam -"av\0" // Avaric -"ae\0" // Avestan -"ay\0" // Aymara -"az\0" // Azerbaijani -"ksf" // Bafia -"ban" // Balinese -"bm\0" // Bambara -"bax" // Bamun -"bn\0" // Bangla -"bas" // Basaa -"ba\0" // Bashkir -"eu\0" // Basque -"bbc" // Batak Toba -"be\0" // Belarusian -"bem" // Bemba -"bez" // Bena -"bho" // Bhojpuri -"bi\0" // Bislama -"byn" // Blin -"brx" // Bodo -"bs\0" // Bosnian -"br\0" // Breton -"bug" // Buginese -"bg\0" // Bulgarian -"my\0" // Burmese -"yue" // Cantonese -"ca\0" // Catalan -"ceb" // Cebuano -"tzm" // Central Atlas Tamazight -"ckb" // Central Kurdish -"ccp" // Chakma -"ch\0" // Chamorro -"ce\0" // Chechen -"chr" // Cherokee -"cic" // Chickasaw -"cgg" // Chiga -"zh\0" // Chinese -"cu\0" // Church -"cv\0" // Chuvash -"ksh" // Colognian -"cop" // Coptic -"kw\0" // Cornish -"co\0" // Corsican -"cr\0" // Cree -"hr\0" // Croatian -"cs\0" // Czech -"da\0" // Danish -"dv\0" // Divehi -"doi" // Dogri -"dua" // Duala -"nl\0" // Dutch -"dz\0" // Dzongkha -"ebu" // Embu -"en\0" // English -"myv" // Erzya -"eo\0" // Esperanto -"et\0" // Estonian -"ee\0" // Ewe -"ewo" // Ewondo -"fo\0" // Faroese -"fj\0" // Fijian -"fil" // Filipino -"fi\0" // Finnish -"fr\0" // French -"fur" // Friulian -"ff\0" // Fulah -"gd\0" // Gaelic -"gaa" // Ga -"gl\0" // Galician -"lg\0" // Ganda -"gez" // Geez -"ka\0" // Georgian -"de\0" // German -"got" // Gothic -"el\0" // Greek -"gn\0" // Guarani -"gu\0" // Gujarati -"guz" // Gusii -"ht\0" // Haitian -"ha\0" // Hausa -"haw" // Hawaiian -"he\0" // Hebrew -"hz\0" // Herero -"hi\0" // Hindi -"ho\0" // Hiri Motu -"hu\0" // Hungarian -"is\0" // Icelandic -"io\0" // Ido -"ig\0" // Igbo -"smn" // Inari Sami -"id\0" // Indonesian -"inh" // Ingush -"ia\0" // Interlingua -"ie\0" // Interlingue -"iu\0" // Inuktitut -"ik\0" // Inupiaq -"ga\0" // Irish -"it\0" // Italian -"ja\0" // Japanese -"jv\0" // Javanese -"kaj" // Jju -"dyo" // Jola Fonyi -"kea" // Kabuverdianu -"kab" // Kabyle -"kkj" // Kako -"kl\0" // Kalaallisut -"kln" // Kalenjin -"kam" // Kamba -"kn\0" // Kannada -"kr\0" // Kanuri -"ks\0" // Kashmiri -"kk\0" // Kazakh -"ken" // Kenyang -"km\0" // Khmer -"quc" // Kiche -"ki\0" // Kikuyu -"rw\0" // Kinyarwanda -"kv\0" // Komi -"kg\0" // Kongo -"kok" // Konkani -"ko\0" // Korean -"kfo" // Koro -"ses" // Koyraboro Senni -"khq" // Koyra Chiini -"kpe" // Kpelle -"kj\0" // Kuanyama -"ku\0" // Kurdish -"nmg" // Kwasio -"ky\0" // Kyrgyz -"lkt" // Lakota -"lag" // Langi -"lo\0" // Lao -"la\0" // Latin -"lv\0" // Latvian -"lez" // Lezghian -"li\0" // Limburgish -"ln\0" // Lingala -"lzh" // Literary Chinese -"lt\0" // Lithuanian -"jbo" // Lojban -"dsb" // Lower Sorbian -"nds" // Low German -"lu\0" // Luba Katanga -"smj" // Lule Sami -"luo" // Luo -"lb\0" // Luxembourgish -"luy" // Luyia -"mk\0" // Macedonian -"jmc" // Machame -"mai" // Maithili -"mgh" // Makhuwa Meetto -"kde" // Makonde -"mg\0" // Malagasy -"ml\0" // Malayalam -"ms\0" // Malay -"mt\0" // Maltese -"man" // Mandingo -"mni" // Manipuri -"gv\0" // Manx -"mi\0" // Maori -"arn" // Mapuche -"mr\0" // Marathi -"mh\0" // Marshallese -"mas" // Masai -"mzn" // Mazanderani -"men" // Mende -"mer" // Meru -"mgo" // Meta -"moh" // Mohawk -"mn\0" // Mongolian -"mfe" // Morisyen -"mua" // Mundang -"mus" // Muscogee -"naq" // Nama -"na\0" // Nauru -"nv\0" // Navajo -"ng\0" // Ndonga -"ne\0" // Nepali -"new" // Newari -"nnh" // Ngiemboon -"jgo" // Ngomba -"pcm" // Nigerian Pidgin -"nqo" // Nko -"lrc" // Northern Luri -"se\0" // Northern Sami -"nso" // Northern Sotho -"nd\0" // North Ndebele -"nb\0" // Norwegian Bokmal -"nn\0" // Norwegian Nynorsk -"nus" // Nuer -"ny\0" // Nyanja -"nyn" // Nyankole -"oc\0" // Occitan -"or\0" // Odia -"oj\0" // Ojibwa -"sga" // Old Irish -"non" // Old Norse -"peo" // Old Persian -"om\0" // Oromo -"osa" // Osage -"os\0" // Ossetic -"pal" // Pahlavi -"pau" // Palauan -"pi\0" // Pali -"pap" // Papiamento -"ps\0" // Pashto -"fa\0" // Persian -"phn" // Phoenician -"pl\0" // Polish -"pt\0" // Portuguese -"prg" // Prussian -"pa\0" // Punjabi -"qu\0" // Quechua -"ro\0" // Romanian -"rm\0" // Romansh -"rof" // Rombo -"rn\0" // Rundi -"ru\0" // Russian -"rwk" // Rwa -"ssy" // Saho -"sah" // Sakha -"saq" // Samburu -"sm\0" // Samoan -"sg\0" // Sango -"sbp" // Sangu -"sa\0" // Sanskrit -"sat" // Santali -"sc\0" // Sardinian -"saz" // Saurashtra -"seh" // Sena -"sr\0" // Serbian -"ksb" // Shambala -"sn\0" // Shona -"ii\0" // Sichuan Yi -"scn" // Sicilian -"sid" // Sidamo -"szl" // Silesian -"sd\0" // Sindhi -"si\0" // Sinhala -"sms" // Skolt Sami -"sk\0" // Slovak -"sl\0" // Slovenian -"xog" // Soga -"so\0" // Somali -"sdh" // Southern Kurdish -"sma" // Southern Sami -"st\0" // Southern Sotho -"nr\0" // South Ndebele -"es\0" // Spanish -"zgh" // Standard Moroccan Tamazight -"su\0" // Sundanese -"sw\0" // Swahili -"ss\0" // Swati -"sv\0" // Swedish -"gsw" // Swiss German -"syr" // Syriac -"shi" // Tachelhit -"ty\0" // Tahitian -"blt" // Tai Dam -"dav" // Taita -"tg\0" // Tajik -"ta\0" // Tamil -"trv" // Taroko -"twq" // Tasawaq -"tt\0" // Tatar -"te\0" // Telugu -"teo" // Teso -"th\0" // Thai -"bo\0" // Tibetan -"tig" // Tigre -"ti\0" // Tigrinya -"tkl" // Tokelau -"tpi" // Tok Pisin -"to\0" // Tongan -"ts\0" // Tsonga -"tn\0" // Tswana -"tr\0" // Turkish -"tk\0" // Turkmen -"tvl" // Tuvalu -"kcg" // Tyap -"uga" // Ugaritic -"uk\0" // Ukrainian -"hsb" // Upper Sorbian -"ur\0" // Urdu -"ug\0" // Uyghur -"uz\0" // Uzbek -"vai" // Vai -"ve\0" // Venda -"vi\0" // Vietnamese -"vo\0" // Volapuk -"vun" // Vunjo -"wa\0" // Walloon -"wae" // Walser -"wbp" // Warlpiri -"cy\0" // Welsh -"bgn" // Western Balochi -"fy\0" // Western Frisian -"wal" // Wolaytta -"wo\0" // Wolof -"xh\0" // Xhosa -"yav" // Yangben -"yi\0" // Yiddish -"yo\0" // Yoruba -"dje" // Zarma -"za\0" // Zhuang -"zu\0" // Zulu -"kgp" // Kaingang -"yrl" // Nheengatu -; +constexpr std::array<LanguageCodeEntry, 330> languageCodeList { + LanguageCodeEntry {{}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}}, // AnyLanguage + LanguageCodeEntry {{}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}}, // C + LanguageCodeEntry {{{'a', 'b'}}, {{'a', 'b', 'k'}}, {{'a', 'b', 'k'}}, {{'a', 'b', 'k'}}}, // Abkhazian + LanguageCodeEntry {{{'a', 'a'}}, {{'a', 'a', 'r'}}, {{'a', 'a', 'r'}}, {{'a', 'a', 'r'}}}, // Afar + LanguageCodeEntry {{{'a', 'f'}}, {{'a', 'f', 'r'}}, {{'a', 'f', 'r'}}, {{'a', 'f', 'r'}}}, // Afrikaans + LanguageCodeEntry {{}, {}, {}, {{'a', 'g', 'q'}}}, // Aghem + LanguageCodeEntry {{{'a', 'k'}}, {{'a', 'k', 'a'}}, {{'a', 'k', 'a'}}, {{'a', 'k', 'a'}}}, // Akan + LanguageCodeEntry {{}, {{'a', 'k', 'k'}}, {{'a', 'k', 'k'}}, {{'a', 'k', 'k'}}}, // Akkadian + LanguageCodeEntry {{}, {}, {}, {{'b', 's', 's'}}}, // Akoose + LanguageCodeEntry {{{'s', 'q'}}, {{'a', 'l', 'b'}}, {{'s', 'q', 'i'}}, {{'s', 'q', 'i'}}}, // Albanian + LanguageCodeEntry {{}, {}, {}, {{'a', 's', 'e'}}}, // American Sign Language + LanguageCodeEntry {{{'a', 'm'}}, {{'a', 'm', 'h'}}, {{'a', 'm', 'h'}}, {{'a', 'm', 'h'}}}, // Amharic + LanguageCodeEntry {{}, {{'e', 'g', 'y'}}, {{'e', 'g', 'y'}}, {{'e', 'g', 'y'}}}, // Ancient Egyptian + LanguageCodeEntry {{}, {{'g', 'r', 'c'}}, {{'g', 'r', 'c'}}, {{'g', 'r', 'c'}}}, // Ancient Greek + LanguageCodeEntry {{{'a', 'r'}}, {{'a', 'r', 'a'}}, {{'a', 'r', 'a'}}, {{'a', 'r', 'a'}}}, // Arabic + LanguageCodeEntry {{{'a', 'n'}}, {{'a', 'r', 'g'}}, {{'a', 'r', 'g'}}, {{'a', 'r', 'g'}}}, // Aragonese + LanguageCodeEntry {{}, {{'a', 'r', 'c'}}, {{'a', 'r', 'c'}}, {{'a', 'r', 'c'}}}, // Aramaic + LanguageCodeEntry {{{'h', 'y'}}, {{'a', 'r', 'm'}}, {{'h', 'y', 'e'}}, {{'h', 'y', 'e'}}}, // Armenian + LanguageCodeEntry {{{'a', 's'}}, {{'a', 's', 'm'}}, {{'a', 's', 'm'}}, {{'a', 's', 'm'}}}, // Assamese + LanguageCodeEntry {{}, {{'a', 's', 't'}}, {{'a', 's', 't'}}, {{'a', 's', 't'}}}, // Asturian + LanguageCodeEntry {{}, {}, {}, {{'a', 's', 'a'}}}, // Asu + LanguageCodeEntry {{}, {}, {}, {{'c', 'c', 'h'}}}, // Atsam + LanguageCodeEntry {{{'a', 'v'}}, {{'a', 'v', 'a'}}, {{'a', 'v', 'a'}}, {{'a', 'v', 'a'}}}, // Avaric + LanguageCodeEntry {{{'a', 'e'}}, {{'a', 'v', 'e'}}, {{'a', 'v', 'e'}}, {{'a', 'v', 'e'}}}, // Avestan + LanguageCodeEntry {{{'a', 'y'}}, {{'a', 'y', 'm'}}, {{'a', 'y', 'm'}}, {{'a', 'y', 'm'}}}, // Aymara + LanguageCodeEntry {{{'a', 'z'}}, {{'a', 'z', 'e'}}, {{'a', 'z', 'e'}}, {{'a', 'z', 'e'}}}, // Azerbaijani + LanguageCodeEntry {{}, {}, {}, {{'k', 's', 'f'}}}, // Bafia + LanguageCodeEntry {{}, {{'b', 'a', 'n'}}, {{'b', 'a', 'n'}}, {{'b', 'a', 'n'}}}, // Balinese + LanguageCodeEntry {{{'b', 'm'}}, {{'b', 'a', 'm'}}, {{'b', 'a', 'm'}}, {{'b', 'a', 'm'}}}, // Bambara + LanguageCodeEntry {{}, {}, {}, {{'b', 'a', 'x'}}}, // Bamun + LanguageCodeEntry {{{'b', 'n'}}, {{'b', 'e', 'n'}}, {{'b', 'e', 'n'}}, {{'b', 'e', 'n'}}}, // Bangla + LanguageCodeEntry {{}, {{'b', 'a', 's'}}, {{'b', 'a', 's'}}, {{'b', 'a', 's'}}}, // Basaa + LanguageCodeEntry {{{'b', 'a'}}, {{'b', 'a', 'k'}}, {{'b', 'a', 'k'}}, {{'b', 'a', 'k'}}}, // Bashkir + LanguageCodeEntry {{{'e', 'u'}}, {{'b', 'a', 'q'}}, {{'e', 'u', 's'}}, {{'e', 'u', 's'}}}, // Basque + LanguageCodeEntry {{}, {}, {}, {{'b', 'b', 'c'}}}, // Batak Toba + LanguageCodeEntry {{{'b', 'e'}}, {{'b', 'e', 'l'}}, {{'b', 'e', 'l'}}, {{'b', 'e', 'l'}}}, // Belarusian + LanguageCodeEntry {{}, {{'b', 'e', 'm'}}, {{'b', 'e', 'm'}}, {{'b', 'e', 'm'}}}, // Bemba + LanguageCodeEntry {{}, {}, {}, {{'b', 'e', 'z'}}}, // Bena + LanguageCodeEntry {{}, {{'b', 'h', 'o'}}, {{'b', 'h', 'o'}}, {{'b', 'h', 'o'}}}, // Bhojpuri + LanguageCodeEntry {{{'b', 'i'}}, {{'b', 'i', 's'}}, {{'b', 'i', 's'}}, {{'b', 'i', 's'}}}, // Bislama + LanguageCodeEntry {{}, {{'b', 'y', 'n'}}, {{'b', 'y', 'n'}}, {{'b', 'y', 'n'}}}, // Blin + LanguageCodeEntry {{}, {}, {}, {{'b', 'r', 'x'}}}, // Bodo + LanguageCodeEntry {{{'b', 's'}}, {{'b', 'o', 's'}}, {{'b', 'o', 's'}}, {{'b', 'o', 's'}}}, // Bosnian + LanguageCodeEntry {{{'b', 'r'}}, {{'b', 'r', 'e'}}, {{'b', 'r', 'e'}}, {{'b', 'r', 'e'}}}, // Breton + LanguageCodeEntry {{}, {{'b', 'u', 'g'}}, {{'b', 'u', 'g'}}, {{'b', 'u', 'g'}}}, // Buginese + LanguageCodeEntry {{{'b', 'g'}}, {{'b', 'u', 'l'}}, {{'b', 'u', 'l'}}, {{'b', 'u', 'l'}}}, // Bulgarian + LanguageCodeEntry {{{'m', 'y'}}, {{'b', 'u', 'r'}}, {{'m', 'y', 'a'}}, {{'m', 'y', 'a'}}}, // Burmese + LanguageCodeEntry {{}, {}, {}, {{'y', 'u', 'e'}}}, // Cantonese + LanguageCodeEntry {{{'c', 'a'}}, {{'c', 'a', 't'}}, {{'c', 'a', 't'}}, {{'c', 'a', 't'}}}, // Catalan + LanguageCodeEntry {{}, {{'c', 'e', 'b'}}, {{'c', 'e', 'b'}}, {{'c', 'e', 'b'}}}, // Cebuano + LanguageCodeEntry {{}, {}, {}, {{'t', 'z', 'm'}}}, // Central Atlas Tamazight + LanguageCodeEntry {{}, {}, {}, {{'c', 'k', 'b'}}}, // Central Kurdish + LanguageCodeEntry {{}, {}, {}, {{'c', 'c', 'p'}}}, // Chakma + LanguageCodeEntry {{{'c', 'h'}}, {{'c', 'h', 'a'}}, {{'c', 'h', 'a'}}, {{'c', 'h', 'a'}}}, // Chamorro + LanguageCodeEntry {{{'c', 'e'}}, {{'c', 'h', 'e'}}, {{'c', 'h', 'e'}}, {{'c', 'h', 'e'}}}, // Chechen + LanguageCodeEntry {{}, {{'c', 'h', 'r'}}, {{'c', 'h', 'r'}}, {{'c', 'h', 'r'}}}, // Cherokee + LanguageCodeEntry {{}, {}, {}, {{'c', 'i', 'c'}}}, // Chickasaw + LanguageCodeEntry {{}, {}, {}, {{'c', 'g', 'g'}}}, // Chiga + LanguageCodeEntry {{{'z', 'h'}}, {{'c', 'h', 'i'}}, {{'z', 'h', 'o'}}, {{'z', 'h', 'o'}}}, // Chinese + LanguageCodeEntry {{{'c', 'u'}}, {{'c', 'h', 'u'}}, {{'c', 'h', 'u'}}, {{'c', 'h', 'u'}}}, // Church + LanguageCodeEntry {{{'c', 'v'}}, {{'c', 'h', 'v'}}, {{'c', 'h', 'v'}}, {{'c', 'h', 'v'}}}, // Chuvash + LanguageCodeEntry {{}, {}, {}, {{'k', 's', 'h'}}}, // Colognian + LanguageCodeEntry {{}, {{'c', 'o', 'p'}}, {{'c', 'o', 'p'}}, {{'c', 'o', 'p'}}}, // Coptic + LanguageCodeEntry {{{'k', 'w'}}, {{'c', 'o', 'r'}}, {{'c', 'o', 'r'}}, {{'c', 'o', 'r'}}}, // Cornish + LanguageCodeEntry {{{'c', 'o'}}, {{'c', 'o', 's'}}, {{'c', 'o', 's'}}, {{'c', 'o', 's'}}}, // Corsican + LanguageCodeEntry {{{'c', 'r'}}, {{'c', 'r', 'e'}}, {{'c', 'r', 'e'}}, {{'c', 'r', 'e'}}}, // Cree + LanguageCodeEntry {{{'h', 'r'}}, {{'h', 'r', 'v'}}, {{'h', 'r', 'v'}}, {{'h', 'r', 'v'}}}, // Croatian + LanguageCodeEntry {{{'c', 's'}}, {{'c', 'z', 'e'}}, {{'c', 'e', 's'}}, {{'c', 'e', 's'}}}, // Czech + LanguageCodeEntry {{{'d', 'a'}}, {{'d', 'a', 'n'}}, {{'d', 'a', 'n'}}, {{'d', 'a', 'n'}}}, // Danish + LanguageCodeEntry {{{'d', 'v'}}, {{'d', 'i', 'v'}}, {{'d', 'i', 'v'}}, {{'d', 'i', 'v'}}}, // Divehi + LanguageCodeEntry {{}, {{'d', 'o', 'i'}}, {{'d', 'o', 'i'}}, {{'d', 'o', 'i'}}}, // Dogri + LanguageCodeEntry {{}, {{'d', 'u', 'a'}}, {{'d', 'u', 'a'}}, {{'d', 'u', 'a'}}}, // Duala + LanguageCodeEntry {{{'n', 'l'}}, {{'d', 'u', 't'}}, {{'n', 'l', 'd'}}, {{'n', 'l', 'd'}}}, // Dutch + LanguageCodeEntry {{{'d', 'z'}}, {{'d', 'z', 'o'}}, {{'d', 'z', 'o'}}, {{'d', 'z', 'o'}}}, // Dzongkha + LanguageCodeEntry {{}, {}, {}, {{'e', 'b', 'u'}}}, // Embu + LanguageCodeEntry {{{'e', 'n'}}, {{'e', 'n', 'g'}}, {{'e', 'n', 'g'}}, {{'e', 'n', 'g'}}}, // English + LanguageCodeEntry {{}, {{'m', 'y', 'v'}}, {{'m', 'y', 'v'}}, {{'m', 'y', 'v'}}}, // Erzya + LanguageCodeEntry {{{'e', 'o'}}, {{'e', 'p', 'o'}}, {{'e', 'p', 'o'}}, {{'e', 'p', 'o'}}}, // Esperanto + LanguageCodeEntry {{{'e', 't'}}, {{'e', 's', 't'}}, {{'e', 's', 't'}}, {{'e', 's', 't'}}}, // Estonian + LanguageCodeEntry {{{'e', 'e'}}, {{'e', 'w', 'e'}}, {{'e', 'w', 'e'}}, {{'e', 'w', 'e'}}}, // Ewe + LanguageCodeEntry {{}, {{'e', 'w', 'o'}}, {{'e', 'w', 'o'}}, {{'e', 'w', 'o'}}}, // Ewondo + LanguageCodeEntry {{{'f', 'o'}}, {{'f', 'a', 'o'}}, {{'f', 'a', 'o'}}, {{'f', 'a', 'o'}}}, // Faroese + LanguageCodeEntry {{{'f', 'j'}}, {{'f', 'i', 'j'}}, {{'f', 'i', 'j'}}, {{'f', 'i', 'j'}}}, // Fijian + LanguageCodeEntry {{}, {{'f', 'i', 'l'}}, {{'f', 'i', 'l'}}, {{'f', 'i', 'l'}}}, // Filipino + LanguageCodeEntry {{{'f', 'i'}}, {{'f', 'i', 'n'}}, {{'f', 'i', 'n'}}, {{'f', 'i', 'n'}}}, // Finnish + LanguageCodeEntry {{{'f', 'r'}}, {{'f', 'r', 'e'}}, {{'f', 'r', 'a'}}, {{'f', 'r', 'a'}}}, // French + LanguageCodeEntry {{}, {{'f', 'u', 'r'}}, {{'f', 'u', 'r'}}, {{'f', 'u', 'r'}}}, // Friulian + LanguageCodeEntry {{{'f', 'f'}}, {{'f', 'u', 'l'}}, {{'f', 'u', 'l'}}, {{'f', 'u', 'l'}}}, // Fulah + LanguageCodeEntry {{{'g', 'd'}}, {{'g', 'l', 'a'}}, {{'g', 'l', 'a'}}, {{'g', 'l', 'a'}}}, // Gaelic + LanguageCodeEntry {{}, {{'g', 'a', 'a'}}, {{'g', 'a', 'a'}}, {{'g', 'a', 'a'}}}, // Ga + LanguageCodeEntry {{{'g', 'l'}}, {{'g', 'l', 'g'}}, {{'g', 'l', 'g'}}, {{'g', 'l', 'g'}}}, // Galician + LanguageCodeEntry {{{'l', 'g'}}, {{'l', 'u', 'g'}}, {{'l', 'u', 'g'}}, {{'l', 'u', 'g'}}}, // Ganda + LanguageCodeEntry {{}, {{'g', 'e', 'z'}}, {{'g', 'e', 'z'}}, {{'g', 'e', 'z'}}}, // Geez + LanguageCodeEntry {{{'k', 'a'}}, {{'g', 'e', 'o'}}, {{'k', 'a', 't'}}, {{'k', 'a', 't'}}}, // Georgian + LanguageCodeEntry {{{'d', 'e'}}, {{'g', 'e', 'r'}}, {{'d', 'e', 'u'}}, {{'d', 'e', 'u'}}}, // German + LanguageCodeEntry {{}, {{'g', 'o', 't'}}, {{'g', 'o', 't'}}, {{'g', 'o', 't'}}}, // Gothic + LanguageCodeEntry {{{'e', 'l'}}, {{'g', 'r', 'e'}}, {{'e', 'l', 'l'}}, {{'e', 'l', 'l'}}}, // Greek + LanguageCodeEntry {{{'g', 'n'}}, {{'g', 'r', 'n'}}, {{'g', 'r', 'n'}}, {{'g', 'r', 'n'}}}, // Guarani + LanguageCodeEntry {{{'g', 'u'}}, {{'g', 'u', 'j'}}, {{'g', 'u', 'j'}}, {{'g', 'u', 'j'}}}, // Gujarati + LanguageCodeEntry {{}, {}, {}, {{'g', 'u', 'z'}}}, // Gusii + LanguageCodeEntry {{{'h', 't'}}, {{'h', 'a', 't'}}, {{'h', 'a', 't'}}, {{'h', 'a', 't'}}}, // Haitian + LanguageCodeEntry {{{'h', 'a'}}, {{'h', 'a', 'u'}}, {{'h', 'a', 'u'}}, {{'h', 'a', 'u'}}}, // Hausa + LanguageCodeEntry {{}, {{'h', 'a', 'w'}}, {{'h', 'a', 'w'}}, {{'h', 'a', 'w'}}}, // Hawaiian + LanguageCodeEntry {{{'h', 'e'}}, {{'h', 'e', 'b'}}, {{'h', 'e', 'b'}}, {{'h', 'e', 'b'}}}, // Hebrew + LanguageCodeEntry {{{'h', 'z'}}, {{'h', 'e', 'r'}}, {{'h', 'e', 'r'}}, {{'h', 'e', 'r'}}}, // Herero + LanguageCodeEntry {{{'h', 'i'}}, {{'h', 'i', 'n'}}, {{'h', 'i', 'n'}}, {{'h', 'i', 'n'}}}, // Hindi + LanguageCodeEntry {{{'h', 'o'}}, {{'h', 'm', 'o'}}, {{'h', 'm', 'o'}}, {{'h', 'm', 'o'}}}, // Hiri Motu + LanguageCodeEntry {{{'h', 'u'}}, {{'h', 'u', 'n'}}, {{'h', 'u', 'n'}}, {{'h', 'u', 'n'}}}, // Hungarian + LanguageCodeEntry {{{'i', 's'}}, {{'i', 'c', 'e'}}, {{'i', 's', 'l'}}, {{'i', 's', 'l'}}}, // Icelandic + LanguageCodeEntry {{{'i', 'o'}}, {{'i', 'd', 'o'}}, {{'i', 'd', 'o'}}, {{'i', 'd', 'o'}}}, // Ido + LanguageCodeEntry {{{'i', 'g'}}, {{'i', 'b', 'o'}}, {{'i', 'b', 'o'}}, {{'i', 'b', 'o'}}}, // Igbo + LanguageCodeEntry {{}, {{'s', 'm', 'n'}}, {{'s', 'm', 'n'}}, {{'s', 'm', 'n'}}}, // Inari Sami + LanguageCodeEntry {{{'i', 'd'}}, {{'i', 'n', 'd'}}, {{'i', 'n', 'd'}}, {{'i', 'n', 'd'}}}, // Indonesian + LanguageCodeEntry {{}, {{'i', 'n', 'h'}}, {{'i', 'n', 'h'}}, {{'i', 'n', 'h'}}}, // Ingush + LanguageCodeEntry {{{'i', 'a'}}, {{'i', 'n', 'a'}}, {{'i', 'n', 'a'}}, {{'i', 'n', 'a'}}}, // Interlingua + LanguageCodeEntry {{{'i', 'e'}}, {{'i', 'l', 'e'}}, {{'i', 'l', 'e'}}, {{'i', 'l', 'e'}}}, // Interlingue + LanguageCodeEntry {{{'i', 'u'}}, {{'i', 'k', 'u'}}, {{'i', 'k', 'u'}}, {{'i', 'k', 'u'}}}, // Inuktitut + LanguageCodeEntry {{{'i', 'k'}}, {{'i', 'p', 'k'}}, {{'i', 'p', 'k'}}, {{'i', 'p', 'k'}}}, // Inupiaq + LanguageCodeEntry {{{'g', 'a'}}, {{'g', 'l', 'e'}}, {{'g', 'l', 'e'}}, {{'g', 'l', 'e'}}}, // Irish + LanguageCodeEntry {{{'i', 't'}}, {{'i', 't', 'a'}}, {{'i', 't', 'a'}}, {{'i', 't', 'a'}}}, // Italian + LanguageCodeEntry {{{'j', 'a'}}, {{'j', 'p', 'n'}}, {{'j', 'p', 'n'}}, {{'j', 'p', 'n'}}}, // Japanese + LanguageCodeEntry {{{'j', 'v'}}, {{'j', 'a', 'v'}}, {{'j', 'a', 'v'}}, {{'j', 'a', 'v'}}}, // Javanese + LanguageCodeEntry {{}, {}, {}, {{'k', 'a', 'j'}}}, // Jju + LanguageCodeEntry {{}, {}, {}, {{'d', 'y', 'o'}}}, // Jola Fonyi + LanguageCodeEntry {{}, {}, {}, {{'k', 'e', 'a'}}}, // Kabuverdianu + LanguageCodeEntry {{}, {{'k', 'a', 'b'}}, {{'k', 'a', 'b'}}, {{'k', 'a', 'b'}}}, // Kabyle + LanguageCodeEntry {{}, {}, {}, {{'k', 'k', 'j'}}}, // Kako + LanguageCodeEntry {{{'k', 'l'}}, {{'k', 'a', 'l'}}, {{'k', 'a', 'l'}}, {{'k', 'a', 'l'}}}, // Kalaallisut + LanguageCodeEntry {{}, {}, {}, {{'k', 'l', 'n'}}}, // Kalenjin + LanguageCodeEntry {{}, {{'k', 'a', 'm'}}, {{'k', 'a', 'm'}}, {{'k', 'a', 'm'}}}, // Kamba + LanguageCodeEntry {{{'k', 'n'}}, {{'k', 'a', 'n'}}, {{'k', 'a', 'n'}}, {{'k', 'a', 'n'}}}, // Kannada + LanguageCodeEntry {{{'k', 'r'}}, {{'k', 'a', 'u'}}, {{'k', 'a', 'u'}}, {{'k', 'a', 'u'}}}, // Kanuri + LanguageCodeEntry {{{'k', 's'}}, {{'k', 'a', 's'}}, {{'k', 'a', 's'}}, {{'k', 'a', 's'}}}, // Kashmiri + LanguageCodeEntry {{{'k', 'k'}}, {{'k', 'a', 'z'}}, {{'k', 'a', 'z'}}, {{'k', 'a', 'z'}}}, // Kazakh + LanguageCodeEntry {{}, {}, {}, {{'k', 'e', 'n'}}}, // Kenyang + LanguageCodeEntry {{{'k', 'm'}}, {{'k', 'h', 'm'}}, {{'k', 'h', 'm'}}, {{'k', 'h', 'm'}}}, // Khmer + LanguageCodeEntry {{}, {}, {}, {{'q', 'u', 'c'}}}, // Kiche + LanguageCodeEntry {{{'k', 'i'}}, {{'k', 'i', 'k'}}, {{'k', 'i', 'k'}}, {{'k', 'i', 'k'}}}, // Kikuyu + LanguageCodeEntry {{{'r', 'w'}}, {{'k', 'i', 'n'}}, {{'k', 'i', 'n'}}, {{'k', 'i', 'n'}}}, // Kinyarwanda + LanguageCodeEntry {{{'k', 'v'}}, {{'k', 'o', 'm'}}, {{'k', 'o', 'm'}}, {{'k', 'o', 'm'}}}, // Komi + LanguageCodeEntry {{{'k', 'g'}}, {{'k', 'o', 'n'}}, {{'k', 'o', 'n'}}, {{'k', 'o', 'n'}}}, // Kongo + LanguageCodeEntry {{}, {{'k', 'o', 'k'}}, {{'k', 'o', 'k'}}, {{'k', 'o', 'k'}}}, // Konkani + LanguageCodeEntry {{{'k', 'o'}}, {{'k', 'o', 'r'}}, {{'k', 'o', 'r'}}, {{'k', 'o', 'r'}}}, // Korean + LanguageCodeEntry {{}, {}, {}, {{'k', 'f', 'o'}}}, // Koro + LanguageCodeEntry {{}, {}, {}, {{'s', 'e', 's'}}}, // Koyraboro Senni + LanguageCodeEntry {{}, {}, {}, {{'k', 'h', 'q'}}}, // Koyra Chiini + LanguageCodeEntry {{}, {{'k', 'p', 'e'}}, {{'k', 'p', 'e'}}, {{'k', 'p', 'e'}}}, // Kpelle + LanguageCodeEntry {{{'k', 'j'}}, {{'k', 'u', 'a'}}, {{'k', 'u', 'a'}}, {{'k', 'u', 'a'}}}, // Kuanyama + LanguageCodeEntry {{{'k', 'u'}}, {{'k', 'u', 'r'}}, {{'k', 'u', 'r'}}, {{'k', 'u', 'r'}}}, // Kurdish + LanguageCodeEntry {{}, {}, {}, {{'n', 'm', 'g'}}}, // Kwasio + LanguageCodeEntry {{{'k', 'y'}}, {{'k', 'i', 'r'}}, {{'k', 'i', 'r'}}, {{'k', 'i', 'r'}}}, // Kyrgyz + LanguageCodeEntry {{}, {}, {}, {{'l', 'k', 't'}}}, // Lakota + LanguageCodeEntry {{}, {}, {}, {{'l', 'a', 'g'}}}, // Langi + LanguageCodeEntry {{{'l', 'o'}}, {{'l', 'a', 'o'}}, {{'l', 'a', 'o'}}, {{'l', 'a', 'o'}}}, // Lao + LanguageCodeEntry {{{'l', 'a'}}, {{'l', 'a', 't'}}, {{'l', 'a', 't'}}, {{'l', 'a', 't'}}}, // Latin + LanguageCodeEntry {{{'l', 'v'}}, {{'l', 'a', 'v'}}, {{'l', 'a', 'v'}}, {{'l', 'a', 'v'}}}, // Latvian + LanguageCodeEntry {{}, {{'l', 'e', 'z'}}, {{'l', 'e', 'z'}}, {{'l', 'e', 'z'}}}, // Lezghian + LanguageCodeEntry {{{'l', 'i'}}, {{'l', 'i', 'm'}}, {{'l', 'i', 'm'}}, {{'l', 'i', 'm'}}}, // Limburgish + LanguageCodeEntry {{{'l', 'n'}}, {{'l', 'i', 'n'}}, {{'l', 'i', 'n'}}, {{'l', 'i', 'n'}}}, // Lingala + LanguageCodeEntry {{}, {}, {}, {{'l', 'z', 'h'}}}, // Literary Chinese + LanguageCodeEntry {{{'l', 't'}}, {{'l', 'i', 't'}}, {{'l', 'i', 't'}}, {{'l', 'i', 't'}}}, // Lithuanian + LanguageCodeEntry {{}, {{'j', 'b', 'o'}}, {{'j', 'b', 'o'}}, {{'j', 'b', 'o'}}}, // Lojban + LanguageCodeEntry {{}, {{'d', 's', 'b'}}, {{'d', 's', 'b'}}, {{'d', 's', 'b'}}}, // Lower Sorbian + LanguageCodeEntry {{}, {{'n', 'd', 's'}}, {{'n', 'd', 's'}}, {{'n', 'd', 's'}}}, // Low German + LanguageCodeEntry {{{'l', 'u'}}, {{'l', 'u', 'b'}}, {{'l', 'u', 'b'}}, {{'l', 'u', 'b'}}}, // Luba Katanga + LanguageCodeEntry {{}, {{'s', 'm', 'j'}}, {{'s', 'm', 'j'}}, {{'s', 'm', 'j'}}}, // Lule Sami + LanguageCodeEntry {{}, {{'l', 'u', 'o'}}, {{'l', 'u', 'o'}}, {{'l', 'u', 'o'}}}, // Luo + LanguageCodeEntry {{{'l', 'b'}}, {{'l', 't', 'z'}}, {{'l', 't', 'z'}}, {{'l', 't', 'z'}}}, // Luxembourgish + LanguageCodeEntry {{}, {}, {}, {{'l', 'u', 'y'}}}, // Luyia + LanguageCodeEntry {{{'m', 'k'}}, {{'m', 'a', 'c'}}, {{'m', 'k', 'd'}}, {{'m', 'k', 'd'}}}, // Macedonian + LanguageCodeEntry {{}, {}, {}, {{'j', 'm', 'c'}}}, // Machame + LanguageCodeEntry {{}, {{'m', 'a', 'i'}}, {{'m', 'a', 'i'}}, {{'m', 'a', 'i'}}}, // Maithili + LanguageCodeEntry {{}, {}, {}, {{'m', 'g', 'h'}}}, // Makhuwa Meetto + LanguageCodeEntry {{}, {}, {}, {{'k', 'd', 'e'}}}, // Makonde + LanguageCodeEntry {{{'m', 'g'}}, {{'m', 'l', 'g'}}, {{'m', 'l', 'g'}}, {{'m', 'l', 'g'}}}, // Malagasy + LanguageCodeEntry {{{'m', 'l'}}, {{'m', 'a', 'l'}}, {{'m', 'a', 'l'}}, {{'m', 'a', 'l'}}}, // Malayalam + LanguageCodeEntry {{{'m', 's'}}, {{'m', 'a', 'y'}}, {{'m', 's', 'a'}}, {{'m', 's', 'a'}}}, // Malay + LanguageCodeEntry {{{'m', 't'}}, {{'m', 'l', 't'}}, {{'m', 'l', 't'}}, {{'m', 'l', 't'}}}, // Maltese + LanguageCodeEntry {{}, {{'m', 'a', 'n'}}, {{'m', 'a', 'n'}}, {{'m', 'a', 'n'}}}, // Mandingo + LanguageCodeEntry {{}, {{'m', 'n', 'i'}}, {{'m', 'n', 'i'}}, {{'m', 'n', 'i'}}}, // Manipuri + LanguageCodeEntry {{{'g', 'v'}}, {{'g', 'l', 'v'}}, {{'g', 'l', 'v'}}, {{'g', 'l', 'v'}}}, // Manx + LanguageCodeEntry {{{'m', 'i'}}, {{'m', 'a', 'o'}}, {{'m', 'r', 'i'}}, {{'m', 'r', 'i'}}}, // Maori + LanguageCodeEntry {{}, {{'a', 'r', 'n'}}, {{'a', 'r', 'n'}}, {{'a', 'r', 'n'}}}, // Mapuche + LanguageCodeEntry {{{'m', 'r'}}, {{'m', 'a', 'r'}}, {{'m', 'a', 'r'}}, {{'m', 'a', 'r'}}}, // Marathi + LanguageCodeEntry {{{'m', 'h'}}, {{'m', 'a', 'h'}}, {{'m', 'a', 'h'}}, {{'m', 'a', 'h'}}}, // Marshallese + LanguageCodeEntry {{}, {{'m', 'a', 's'}}, {{'m', 'a', 's'}}, {{'m', 'a', 's'}}}, // Masai + LanguageCodeEntry {{}, {}, {}, {{'m', 'z', 'n'}}}, // Mazanderani + LanguageCodeEntry {{}, {{'m', 'e', 'n'}}, {{'m', 'e', 'n'}}, {{'m', 'e', 'n'}}}, // Mende + LanguageCodeEntry {{}, {}, {}, {{'m', 'e', 'r'}}}, // Meru + LanguageCodeEntry {{}, {}, {}, {{'m', 'g', 'o'}}}, // Meta + LanguageCodeEntry {{}, {{'m', 'o', 'h'}}, {{'m', 'o', 'h'}}, {{'m', 'o', 'h'}}}, // Mohawk + LanguageCodeEntry {{{'m', 'n'}}, {{'m', 'o', 'n'}}, {{'m', 'o', 'n'}}, {{'m', 'o', 'n'}}}, // Mongolian + LanguageCodeEntry {{}, {}, {}, {{'m', 'f', 'e'}}}, // Morisyen + LanguageCodeEntry {{}, {}, {}, {{'m', 'u', 'a'}}}, // Mundang + LanguageCodeEntry {{}, {{'m', 'u', 's'}}, {{'m', 'u', 's'}}, {{'m', 'u', 's'}}}, // Muscogee + LanguageCodeEntry {{}, {}, {}, {{'n', 'a', 'q'}}}, // Nama + LanguageCodeEntry {{{'n', 'a'}}, {{'n', 'a', 'u'}}, {{'n', 'a', 'u'}}, {{'n', 'a', 'u'}}}, // Nauru + LanguageCodeEntry {{{'n', 'v'}}, {{'n', 'a', 'v'}}, {{'n', 'a', 'v'}}, {{'n', 'a', 'v'}}}, // Navajo + LanguageCodeEntry {{{'n', 'g'}}, {{'n', 'd', 'o'}}, {{'n', 'd', 'o'}}, {{'n', 'd', 'o'}}}, // Ndonga + LanguageCodeEntry {{{'n', 'e'}}, {{'n', 'e', 'p'}}, {{'n', 'e', 'p'}}, {{'n', 'e', 'p'}}}, // Nepali + LanguageCodeEntry {{}, {{'n', 'e', 'w'}}, {{'n', 'e', 'w'}}, {{'n', 'e', 'w'}}}, // Newari + LanguageCodeEntry {{}, {}, {}, {{'n', 'n', 'h'}}}, // Ngiemboon + LanguageCodeEntry {{}, {}, {}, {{'j', 'g', 'o'}}}, // Ngomba + LanguageCodeEntry {{}, {}, {}, {{'p', 'c', 'm'}}}, // Nigerian Pidgin + LanguageCodeEntry {{}, {{'n', 'q', 'o'}}, {{'n', 'q', 'o'}}, {{'n', 'q', 'o'}}}, // Nko + LanguageCodeEntry {{}, {}, {}, {{'l', 'r', 'c'}}}, // Northern Luri + LanguageCodeEntry {{{'s', 'e'}}, {{'s', 'm', 'e'}}, {{'s', 'm', 'e'}}, {{'s', 'm', 'e'}}}, // Northern Sami + LanguageCodeEntry {{}, {{'n', 's', 'o'}}, {{'n', 's', 'o'}}, {{'n', 's', 'o'}}}, // Northern Sotho + LanguageCodeEntry {{{'n', 'd'}}, {{'n', 'd', 'e'}}, {{'n', 'd', 'e'}}, {{'n', 'd', 'e'}}}, // North Ndebele + LanguageCodeEntry {{{'n', 'b'}}, {{'n', 'o', 'b'}}, {{'n', 'o', 'b'}}, {{'n', 'o', 'b'}}}, // Norwegian Bokmal + LanguageCodeEntry {{{'n', 'n'}}, {{'n', 'n', 'o'}}, {{'n', 'n', 'o'}}, {{'n', 'n', 'o'}}}, // Norwegian Nynorsk + LanguageCodeEntry {{}, {}, {}, {{'n', 'u', 's'}}}, // Nuer + LanguageCodeEntry {{{'n', 'y'}}, {{'n', 'y', 'a'}}, {{'n', 'y', 'a'}}, {{'n', 'y', 'a'}}}, // Nyanja + LanguageCodeEntry {{}, {{'n', 'y', 'n'}}, {{'n', 'y', 'n'}}, {{'n', 'y', 'n'}}}, // Nyankole + LanguageCodeEntry {{{'o', 'c'}}, {{'o', 'c', 'i'}}, {{'o', 'c', 'i'}}, {{'o', 'c', 'i'}}}, // Occitan + LanguageCodeEntry {{{'o', 'r'}}, {{'o', 'r', 'i'}}, {{'o', 'r', 'i'}}, {{'o', 'r', 'i'}}}, // Odia + LanguageCodeEntry {{{'o', 'j'}}, {{'o', 'j', 'i'}}, {{'o', 'j', 'i'}}, {{'o', 'j', 'i'}}}, // Ojibwa + LanguageCodeEntry {{}, {{'s', 'g', 'a'}}, {{'s', 'g', 'a'}}, {{'s', 'g', 'a'}}}, // Old Irish + LanguageCodeEntry {{}, {{'n', 'o', 'n'}}, {{'n', 'o', 'n'}}, {{'n', 'o', 'n'}}}, // Old Norse + LanguageCodeEntry {{}, {{'p', 'e', 'o'}}, {{'p', 'e', 'o'}}, {{'p', 'e', 'o'}}}, // Old Persian + LanguageCodeEntry {{{'o', 'm'}}, {{'o', 'r', 'm'}}, {{'o', 'r', 'm'}}, {{'o', 'r', 'm'}}}, // Oromo + LanguageCodeEntry {{}, {{'o', 's', 'a'}}, {{'o', 's', 'a'}}, {{'o', 's', 'a'}}}, // Osage + LanguageCodeEntry {{{'o', 's'}}, {{'o', 's', 's'}}, {{'o', 's', 's'}}, {{'o', 's', 's'}}}, // Ossetic + LanguageCodeEntry {{}, {{'p', 'a', 'l'}}, {{'p', 'a', 'l'}}, {{'p', 'a', 'l'}}}, // Pahlavi + LanguageCodeEntry {{}, {{'p', 'a', 'u'}}, {{'p', 'a', 'u'}}, {{'p', 'a', 'u'}}}, // Palauan + LanguageCodeEntry {{{'p', 'i'}}, {{'p', 'l', 'i'}}, {{'p', 'l', 'i'}}, {{'p', 'l', 'i'}}}, // Pali + LanguageCodeEntry {{}, {{'p', 'a', 'p'}}, {{'p', 'a', 'p'}}, {{'p', 'a', 'p'}}}, // Papiamento + LanguageCodeEntry {{{'p', 's'}}, {{'p', 'u', 's'}}, {{'p', 'u', 's'}}, {{'p', 'u', 's'}}}, // Pashto + LanguageCodeEntry {{{'f', 'a'}}, {{'p', 'e', 'r'}}, {{'f', 'a', 's'}}, {{'f', 'a', 's'}}}, // Persian + LanguageCodeEntry {{}, {{'p', 'h', 'n'}}, {{'p', 'h', 'n'}}, {{'p', 'h', 'n'}}}, // Phoenician + LanguageCodeEntry {{{'p', 'l'}}, {{'p', 'o', 'l'}}, {{'p', 'o', 'l'}}, {{'p', 'o', 'l'}}}, // Polish + LanguageCodeEntry {{{'p', 't'}}, {{'p', 'o', 'r'}}, {{'p', 'o', 'r'}}, {{'p', 'o', 'r'}}}, // Portuguese + LanguageCodeEntry {{}, {}, {}, {{'p', 'r', 'g'}}}, // Prussian + LanguageCodeEntry {{{'p', 'a'}}, {{'p', 'a', 'n'}}, {{'p', 'a', 'n'}}, {{'p', 'a', 'n'}}}, // Punjabi + LanguageCodeEntry {{{'q', 'u'}}, {{'q', 'u', 'e'}}, {{'q', 'u', 'e'}}, {{'q', 'u', 'e'}}}, // Quechua + LanguageCodeEntry {{{'r', 'o'}}, {{'r', 'u', 'm'}}, {{'r', 'o', 'n'}}, {{'r', 'o', 'n'}}}, // Romanian + LanguageCodeEntry {{{'r', 'm'}}, {{'r', 'o', 'h'}}, {{'r', 'o', 'h'}}, {{'r', 'o', 'h'}}}, // Romansh + LanguageCodeEntry {{}, {}, {}, {{'r', 'o', 'f'}}}, // Rombo + LanguageCodeEntry {{{'r', 'n'}}, {{'r', 'u', 'n'}}, {{'r', 'u', 'n'}}, {{'r', 'u', 'n'}}}, // Rundi + LanguageCodeEntry {{{'r', 'u'}}, {{'r', 'u', 's'}}, {{'r', 'u', 's'}}, {{'r', 'u', 's'}}}, // Russian + LanguageCodeEntry {{}, {}, {}, {{'r', 'w', 'k'}}}, // Rwa + LanguageCodeEntry {{}, {}, {}, {{'s', 's', 'y'}}}, // Saho + LanguageCodeEntry {{}, {{'s', 'a', 'h'}}, {{'s', 'a', 'h'}}, {{'s', 'a', 'h'}}}, // Sakha + LanguageCodeEntry {{}, {}, {}, {{'s', 'a', 'q'}}}, // Samburu + LanguageCodeEntry {{{'s', 'm'}}, {{'s', 'm', 'o'}}, {{'s', 'm', 'o'}}, {{'s', 'm', 'o'}}}, // Samoan + LanguageCodeEntry {{{'s', 'g'}}, {{'s', 'a', 'g'}}, {{'s', 'a', 'g'}}, {{'s', 'a', 'g'}}}, // Sango + LanguageCodeEntry {{}, {}, {}, {{'s', 'b', 'p'}}}, // Sangu + LanguageCodeEntry {{{'s', 'a'}}, {{'s', 'a', 'n'}}, {{'s', 'a', 'n'}}, {{'s', 'a', 'n'}}}, // Sanskrit + LanguageCodeEntry {{}, {{'s', 'a', 't'}}, {{'s', 'a', 't'}}, {{'s', 'a', 't'}}}, // Santali + LanguageCodeEntry {{{'s', 'c'}}, {{'s', 'r', 'd'}}, {{'s', 'r', 'd'}}, {{'s', 'r', 'd'}}}, // Sardinian + LanguageCodeEntry {{}, {}, {}, {{'s', 'a', 'z'}}}, // Saurashtra + LanguageCodeEntry {{}, {}, {}, {{'s', 'e', 'h'}}}, // Sena + LanguageCodeEntry {{{'s', 'r'}}, {{'s', 'r', 'p'}}, {{'s', 'r', 'p'}}, {{'s', 'r', 'p'}}}, // Serbian + LanguageCodeEntry {{}, {}, {}, {{'k', 's', 'b'}}}, // Shambala + LanguageCodeEntry {{{'s', 'n'}}, {{'s', 'n', 'a'}}, {{'s', 'n', 'a'}}, {{'s', 'n', 'a'}}}, // Shona + LanguageCodeEntry {{{'i', 'i'}}, {{'i', 'i', 'i'}}, {{'i', 'i', 'i'}}, {{'i', 'i', 'i'}}}, // Sichuan Yi + LanguageCodeEntry {{}, {{'s', 'c', 'n'}}, {{'s', 'c', 'n'}}, {{'s', 'c', 'n'}}}, // Sicilian + LanguageCodeEntry {{}, {{'s', 'i', 'd'}}, {{'s', 'i', 'd'}}, {{'s', 'i', 'd'}}}, // Sidamo + LanguageCodeEntry {{}, {}, {}, {{'s', 'z', 'l'}}}, // Silesian + LanguageCodeEntry {{{'s', 'd'}}, {{'s', 'n', 'd'}}, {{'s', 'n', 'd'}}, {{'s', 'n', 'd'}}}, // Sindhi + LanguageCodeEntry {{{'s', 'i'}}, {{'s', 'i', 'n'}}, {{'s', 'i', 'n'}}, {{'s', 'i', 'n'}}}, // Sinhala + LanguageCodeEntry {{}, {{'s', 'm', 's'}}, {{'s', 'm', 's'}}, {{'s', 'm', 's'}}}, // Skolt Sami + LanguageCodeEntry {{{'s', 'k'}}, {{'s', 'l', 'o'}}, {{'s', 'l', 'k'}}, {{'s', 'l', 'k'}}}, // Slovak + LanguageCodeEntry {{{'s', 'l'}}, {{'s', 'l', 'v'}}, {{'s', 'l', 'v'}}, {{'s', 'l', 'v'}}}, // Slovenian + LanguageCodeEntry {{}, {}, {}, {{'x', 'o', 'g'}}}, // Soga + LanguageCodeEntry {{{'s', 'o'}}, {{'s', 'o', 'm'}}, {{'s', 'o', 'm'}}, {{'s', 'o', 'm'}}}, // Somali + LanguageCodeEntry {{}, {}, {}, {{'s', 'd', 'h'}}}, // Southern Kurdish + LanguageCodeEntry {{}, {{'s', 'm', 'a'}}, {{'s', 'm', 'a'}}, {{'s', 'm', 'a'}}}, // Southern Sami + LanguageCodeEntry {{{'s', 't'}}, {{'s', 'o', 't'}}, {{'s', 'o', 't'}}, {{'s', 'o', 't'}}}, // Southern Sotho + LanguageCodeEntry {{{'n', 'r'}}, {{'n', 'b', 'l'}}, {{'n', 'b', 'l'}}, {{'n', 'b', 'l'}}}, // South Ndebele + LanguageCodeEntry {{{'e', 's'}}, {{'s', 'p', 'a'}}, {{'s', 'p', 'a'}}, {{'s', 'p', 'a'}}}, // Spanish + LanguageCodeEntry {{}, {{'z', 'g', 'h'}}, {{'z', 'g', 'h'}}, {{'z', 'g', 'h'}}}, // Standard Moroccan Tamazight + LanguageCodeEntry {{{'s', 'u'}}, {{'s', 'u', 'n'}}, {{'s', 'u', 'n'}}, {{'s', 'u', 'n'}}}, // Sundanese + LanguageCodeEntry {{{'s', 'w'}}, {{'s', 'w', 'a'}}, {{'s', 'w', 'a'}}, {{'s', 'w', 'a'}}}, // Swahili + LanguageCodeEntry {{{'s', 's'}}, {{'s', 's', 'w'}}, {{'s', 's', 'w'}}, {{'s', 's', 'w'}}}, // Swati + LanguageCodeEntry {{{'s', 'v'}}, {{'s', 'w', 'e'}}, {{'s', 'w', 'e'}}, {{'s', 'w', 'e'}}}, // Swedish + LanguageCodeEntry {{}, {{'g', 's', 'w'}}, {{'g', 's', 'w'}}, {{'g', 's', 'w'}}}, // Swiss German + LanguageCodeEntry {{}, {{'s', 'y', 'r'}}, {{'s', 'y', 'r'}}, {{'s', 'y', 'r'}}}, // Syriac + LanguageCodeEntry {{}, {}, {}, {{'s', 'h', 'i'}}}, // Tachelhit + LanguageCodeEntry {{{'t', 'y'}}, {{'t', 'a', 'h'}}, {{'t', 'a', 'h'}}, {{'t', 'a', 'h'}}}, // Tahitian + LanguageCodeEntry {{}, {}, {}, {{'b', 'l', 't'}}}, // Tai Dam + LanguageCodeEntry {{}, {}, {}, {{'d', 'a', 'v'}}}, // Taita + LanguageCodeEntry {{{'t', 'g'}}, {{'t', 'g', 'k'}}, {{'t', 'g', 'k'}}, {{'t', 'g', 'k'}}}, // Tajik + LanguageCodeEntry {{{'t', 'a'}}, {{'t', 'a', 'm'}}, {{'t', 'a', 'm'}}, {{'t', 'a', 'm'}}}, // Tamil + LanguageCodeEntry {{}, {}, {}, {{'t', 'r', 'v'}}}, // Taroko + LanguageCodeEntry {{}, {}, {}, {{'t', 'w', 'q'}}}, // Tasawaq + LanguageCodeEntry {{{'t', 't'}}, {{'t', 'a', 't'}}, {{'t', 'a', 't'}}, {{'t', 'a', 't'}}}, // Tatar + LanguageCodeEntry {{{'t', 'e'}}, {{'t', 'e', 'l'}}, {{'t', 'e', 'l'}}, {{'t', 'e', 'l'}}}, // Telugu + LanguageCodeEntry {{}, {}, {}, {{'t', 'e', 'o'}}}, // Teso + LanguageCodeEntry {{{'t', 'h'}}, {{'t', 'h', 'a'}}, {{'t', 'h', 'a'}}, {{'t', 'h', 'a'}}}, // Thai + LanguageCodeEntry {{{'b', 'o'}}, {{'t', 'i', 'b'}}, {{'b', 'o', 'd'}}, {{'b', 'o', 'd'}}}, // Tibetan + LanguageCodeEntry {{}, {{'t', 'i', 'g'}}, {{'t', 'i', 'g'}}, {{'t', 'i', 'g'}}}, // Tigre + LanguageCodeEntry {{{'t', 'i'}}, {{'t', 'i', 'r'}}, {{'t', 'i', 'r'}}, {{'t', 'i', 'r'}}}, // Tigrinya + LanguageCodeEntry {{}, {{'t', 'k', 'l'}}, {{'t', 'k', 'l'}}, {{'t', 'k', 'l'}}}, // Tokelau + LanguageCodeEntry {{}, {{'t', 'p', 'i'}}, {{'t', 'p', 'i'}}, {{'t', 'p', 'i'}}}, // Tok Pisin + LanguageCodeEntry {{{'t', 'o'}}, {{'t', 'o', 'n'}}, {{'t', 'o', 'n'}}, {{'t', 'o', 'n'}}}, // Tongan + LanguageCodeEntry {{{'t', 's'}}, {{'t', 's', 'o'}}, {{'t', 's', 'o'}}, {{'t', 's', 'o'}}}, // Tsonga + LanguageCodeEntry {{{'t', 'n'}}, {{'t', 's', 'n'}}, {{'t', 's', 'n'}}, {{'t', 's', 'n'}}}, // Tswana + LanguageCodeEntry {{{'t', 'r'}}, {{'t', 'u', 'r'}}, {{'t', 'u', 'r'}}, {{'t', 'u', 'r'}}}, // Turkish + LanguageCodeEntry {{{'t', 'k'}}, {{'t', 'u', 'k'}}, {{'t', 'u', 'k'}}, {{'t', 'u', 'k'}}}, // Turkmen + LanguageCodeEntry {{}, {{'t', 'v', 'l'}}, {{'t', 'v', 'l'}}, {{'t', 'v', 'l'}}}, // Tuvalu + LanguageCodeEntry {{}, {}, {}, {{'k', 'c', 'g'}}}, // Tyap + LanguageCodeEntry {{}, {{'u', 'g', 'a'}}, {{'u', 'g', 'a'}}, {{'u', 'g', 'a'}}}, // Ugaritic + LanguageCodeEntry {{{'u', 'k'}}, {{'u', 'k', 'r'}}, {{'u', 'k', 'r'}}, {{'u', 'k', 'r'}}}, // Ukrainian + LanguageCodeEntry {{}, {{'h', 's', 'b'}}, {{'h', 's', 'b'}}, {{'h', 's', 'b'}}}, // Upper Sorbian + LanguageCodeEntry {{{'u', 'r'}}, {{'u', 'r', 'd'}}, {{'u', 'r', 'd'}}, {{'u', 'r', 'd'}}}, // Urdu + LanguageCodeEntry {{{'u', 'g'}}, {{'u', 'i', 'g'}}, {{'u', 'i', 'g'}}, {{'u', 'i', 'g'}}}, // Uyghur + LanguageCodeEntry {{{'u', 'z'}}, {{'u', 'z', 'b'}}, {{'u', 'z', 'b'}}, {{'u', 'z', 'b'}}}, // Uzbek + LanguageCodeEntry {{}, {{'v', 'a', 'i'}}, {{'v', 'a', 'i'}}, {{'v', 'a', 'i'}}}, // Vai + LanguageCodeEntry {{{'v', 'e'}}, {{'v', 'e', 'n'}}, {{'v', 'e', 'n'}}, {{'v', 'e', 'n'}}}, // Venda + LanguageCodeEntry {{{'v', 'i'}}, {{'v', 'i', 'e'}}, {{'v', 'i', 'e'}}, {{'v', 'i', 'e'}}}, // Vietnamese + LanguageCodeEntry {{{'v', 'o'}}, {{'v', 'o', 'l'}}, {{'v', 'o', 'l'}}, {{'v', 'o', 'l'}}}, // Volapuk + LanguageCodeEntry {{}, {}, {}, {{'v', 'u', 'n'}}}, // Vunjo + LanguageCodeEntry {{{'w', 'a'}}, {{'w', 'l', 'n'}}, {{'w', 'l', 'n'}}, {{'w', 'l', 'n'}}}, // Walloon + LanguageCodeEntry {{}, {}, {}, {{'w', 'a', 'e'}}}, // Walser + LanguageCodeEntry {{}, {}, {}, {{'w', 'b', 'p'}}}, // Warlpiri + LanguageCodeEntry {{{'c', 'y'}}, {{'w', 'e', 'l'}}, {{'c', 'y', 'm'}}, {{'c', 'y', 'm'}}}, // Welsh + LanguageCodeEntry {{}, {}, {}, {{'b', 'g', 'n'}}}, // Western Balochi + LanguageCodeEntry {{{'f', 'y'}}, {{'f', 'r', 'y'}}, {{'f', 'r', 'y'}}, {{'f', 'r', 'y'}}}, // Western Frisian + LanguageCodeEntry {{}, {{'w', 'a', 'l'}}, {{'w', 'a', 'l'}}, {{'w', 'a', 'l'}}}, // Wolaytta + LanguageCodeEntry {{{'w', 'o'}}, {{'w', 'o', 'l'}}, {{'w', 'o', 'l'}}, {{'w', 'o', 'l'}}}, // Wolof + LanguageCodeEntry {{{'x', 'h'}}, {{'x', 'h', 'o'}}, {{'x', 'h', 'o'}}, {{'x', 'h', 'o'}}}, // Xhosa + LanguageCodeEntry {{}, {}, {}, {{'y', 'a', 'v'}}}, // Yangben + LanguageCodeEntry {{{'y', 'i'}}, {{'y', 'i', 'd'}}, {{'y', 'i', 'd'}}, {{'y', 'i', 'd'}}}, // Yiddish + LanguageCodeEntry {{{'y', 'o'}}, {{'y', 'o', 'r'}}, {{'y', 'o', 'r'}}, {{'y', 'o', 'r'}}}, // Yoruba + LanguageCodeEntry {{}, {}, {}, {{'d', 'j', 'e'}}}, // Zarma + LanguageCodeEntry {{{'z', 'a'}}, {{'z', 'h', 'a'}}, {{'z', 'h', 'a'}}, {{'z', 'h', 'a'}}}, // Zhuang + LanguageCodeEntry {{{'z', 'u'}}, {{'z', 'u', 'l'}}, {{'z', 'u', 'l'}}, {{'z', 'u', 'l'}}}, // Zulu + LanguageCodeEntry {{}, {}, {}, {{'k', 'g', 'p'}}}, // Kaingang + LanguageCodeEntry {{}, {}, {}, {{'y', 'r', 'l'}}}, // Nheengatu +}; static const unsigned char script_code_list[] = "Zzzz" // AnyScript diff --git a/src/corelib/text/qlocale_mac.mm b/src/corelib/text/qlocale_mac.mm index ef9d1d24a3..7d75db31d0 100644 --- a/src/corelib/text/qlocale_mac.mm +++ b/src/corelib/text/qlocale_mac.mm @@ -548,12 +548,18 @@ static QVariant getLocaleValue(CFStringRef key) return QVariant(); } +static QLocale::Language codeToLanguage(QStringView s) +{ + return QLocalePrivate::codeToLanguage(s); +} + QVariant QSystemLocale::query(QueryType type, QVariant in) const { QMacAutoReleasePool pool; + switch(type) { case LanguageId: - return getLocaleValue<QLocalePrivate::codeToLanguage>(kCFLocaleLanguageCode); + return getLocaleValue<codeToLanguage>(kCFLocaleLanguageCode); case TerritoryId: return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode); case ScriptId: diff --git a/src/corelib/text/qlocale_p.h b/src/corelib/text/qlocale_p.h index 60bc78db04..5326831bb6 100644 --- a/src/corelib/text/qlocale_p.h +++ b/src/corelib/text/qlocale_p.h @@ -418,18 +418,25 @@ public: [[nodiscard]] QByteArray bcp47Name(char separator = '-') const; - [[nodiscard]] inline QLatin1String languageCode() const - { return languageToCode(QLocale::Language(m_data->m_language_id)); } + [[nodiscard]] inline QLatin1String + languageCode(QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) const + { + return languageToCode(QLocale::Language(m_data->m_language_id), codeTypes); + } [[nodiscard]] inline QLatin1String scriptCode() const { return scriptToCode(QLocale::Script(m_data->m_script_id)); } [[nodiscard]] inline QLatin1String territoryCode() const { return territoryToCode(QLocale::Territory(m_data->m_territory_id)); } [[nodiscard]] static const QLocalePrivate *get(const QLocale &l) { return l.d; } - [[nodiscard]] static QLatin1String languageToCode(QLocale::Language language); + [[nodiscard]] static QLatin1String + languageToCode(QLocale::Language language, + QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode); [[nodiscard]] static QLatin1String scriptToCode(QLocale::Script script); [[nodiscard]] static QLatin1String territoryToCode(QLocale::Territory territory); - [[nodiscard]] static QLocale::Language codeToLanguage(QStringView code) noexcept; + [[nodiscard]] static QLocale::Language + codeToLanguage(QStringView code, + QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) noexcept; [[nodiscard]] static QLocale::Script codeToScript(QStringView code) noexcept; [[nodiscard]] static QLocale::Territory codeToTerritory(QStringView code) noexcept; diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index 2a752e486f..52224701f6 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -3501,6 +3501,23 @@ void tst_QLocale::lcsToCode() QCOMPARE(QLocale::languageToCode(QLocale::AnyLanguage), QString()); QCOMPARE(QLocale::languageToCode(QLocale::C), QString("C")); QCOMPARE(QLocale::languageToCode(QLocale::English), QString("en")); + QCOMPARE(QLocale::languageToCode(QLocale::Albanian), u"sq"_qs); + QCOMPARE(QLocale::languageToCode(QLocale::Albanian, QLocale::ISO639Part1), u"sq"_qs); + QCOMPARE(QLocale::languageToCode(QLocale::Albanian, QLocale::ISO639Part2B), u"alb"_qs); + QCOMPARE(QLocale::languageToCode(QLocale::Albanian, QLocale::ISO639Part2T), u"sqi"_qs); + QCOMPARE(QLocale::languageToCode(QLocale::Albanian, QLocale::ISO639Part3), u"sqi"_qs); + + QCOMPARE(QLocale::languageToCode(QLocale::Taita), u"dav"_qs); + QCOMPARE(QLocale::languageToCode(QLocale::Taita, + QLocale::ISO639Part1 | QLocale::ISO639Part2B + | QLocale::ISO639Part2T), + QString()); + QCOMPARE(QLocale::languageToCode(QLocale::Taita, QLocale::ISO639Part3), u"dav"_qs); + QCOMPARE(QLocale::languageToCode(QLocale::English, QLocale::LanguageCodeTypes {}), QString()); + + // Legacy codes can only be used to convert them to Language values, not other way around. + QCOMPARE(QLocale::languageToCode(QLocale::NorwegianBokmal, QLocale::LegacyLanguageCode), + QString()); QCOMPARE(QLocale::territoryToCode(QLocale::AnyTerritory), QString()); QCOMPARE(QLocale::territoryToCode(QLocale::UnitedStates), QString("US")); @@ -3518,9 +3535,28 @@ void tst_QLocale::codeToLcs() QCOMPARE(QLocale::codeToLanguage(QString("e")), QLocale::AnyLanguage); QCOMPARE(QLocale::codeToLanguage(QString("en")), QLocale::English); QCOMPARE(QLocale::codeToLanguage(QString("EN")), QLocale::English); - QCOMPARE(QLocale::codeToLanguage(QString("eng")), QLocale::AnyLanguage); + QCOMPARE(QLocale::codeToLanguage(QString("eng")), QLocale::English); QCOMPARE(QLocale::codeToLanguage(QString("ha")), QLocale::Hausa); + QCOMPARE(QLocale::codeToLanguage(QString("ha"), QLocale::ISO639Alpha3), QLocale::AnyLanguage); QCOMPARE(QLocale::codeToLanguage(QString("haw")), QLocale::Hawaiian); + QCOMPARE(QLocale::codeToLanguage(QString("haw"), QLocale::ISO639Alpha2), QLocale::AnyLanguage); + + QCOMPARE(QLocale::codeToLanguage(u"sq"), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"alb"), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"sqi"), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"sq", QLocale::ISO639Part1), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"sq", QLocale::ISO639Part3), QLocale::AnyLanguage); + QCOMPARE(QLocale::codeToLanguage(u"alb", QLocale::ISO639Part2B), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"alb", QLocale::ISO639Part2T | QLocale::ISO639Part3), + QLocale::AnyLanguage); + QCOMPARE(QLocale::codeToLanguage(u"sqi", QLocale::ISO639Part2T), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"sqi", QLocale::ISO639Part3), QLocale::Albanian); + QCOMPARE(QLocale::codeToLanguage(u"sqi", QLocale::ISO639Part1 | QLocale::ISO639Part2B), + QLocale::AnyLanguage); + + // Legacy code + QCOMPARE(QLocale::codeToLanguage(u"no"), QLocale::NorwegianBokmal); + QCOMPARE(QLocale::codeToLanguage(u"no", QLocale::ISO639Part1), QLocale::AnyLanguage); QCOMPARE(QLocale::codeToTerritory(QString()), QLocale::AnyTerritory); QCOMPARE(QLocale::codeToTerritory(QString("ZZ")), QLocale::AnyTerritory); diff --git a/util/locale_database/iso639_3.py b/util/locale_database/iso639_3.py new file mode 100644 index 0000000000..b150855ba9 --- /dev/null +++ b/util/locale_database/iso639_3.py @@ -0,0 +1,105 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the locale database tools of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from dataclasses import dataclass +from typing import Dict, Optional + + +@dataclass +class LanguageCodeEntry: + part3Code: str + part2BCode: Optional[str] + part2TCode: Optional[str] + part1Code: Optional[str] + + def id(self) -> str: + if self.part1Code: + return self.part1Code + if self.part2BCode: + return self.part2BCode + return self.part3Code + + def __repr__(self) -> str: + parts = [f'{self.__class__.__name__}({self.id()!r}, part3Code={self.part3Code!r}'] + if self.part2BCode is not None and self.part2BCode != self.part3Code: + parts.append(f', part2BCode={self.part2BCode!r}') + if self.part2TCode != self.part2BCode: + parts.append(f', part2TCode={self.part2TCode!r}') + if self.part1Code is not None: + parts.append(f', part1Code={self.part1Code!r}') + parts.append(')') + return ''.join(parts) + + +class LanguageCodeData: + """ + Representation of ISO639-2 language code data. + """ + def __init__(self, fileName: str): + """ + Construct the object populating the data from the given file. + """ + self.__codeMap: Dict[str, LanguageCodeEntry] = {} + + with open(fileName, 'r', encoding='utf-8') as stream: + stream.readline() # skip the header + for line in stream.readlines(): + part3Code, part2BCode, part2TCode, part1Code, _ = line.split('\t', 4) + + # sanity checks + assert all(p.isascii() for p in (part3Code, part2BCode, part2TCode, part1Code)), \ + f'Non-ascii characters in code names: {part3Code!r} {part2BCode!r} '\ + f'{part2TCode!r} {part1Code!r}' + + assert len(part3Code) == 3, f'Invalid Part 3 code length for {part3Code!r}' + assert not part1Code or len(part1Code) == 2, \ + f'Invalid Part 1 code length for {part3Code!r}: {part1Code!r}' + assert not part2BCode or len(part2BCode) == 3, \ + f'Invalid Part 2B code length for {part3Code!r}: {part2BCode!r}' + assert not part2TCode or len(part2TCode) == 3, \ + f'Invalid Part 2T code length for {part3Code!r}: {part2TCode!r}' + + assert (part2BCode == '') == (part2TCode == ''), \ + f'Only one Part 2 code is specified for {part3Code!r}: ' \ + f'{part2BCode!r} vs {part2TCode!r}' + assert not part2TCode or part2TCode == part3Code, \ + f'Part 3 code {part3Code!r} does not match Part 2T code {part2TCode!r}' + + entry = LanguageCodeEntry(part3Code, part2BCode or None, + part2TCode or None, part1Code or None) + + self.__codeMap[entry.id()] = entry + + def query(self, code: str) -> Optional[LanguageCodeEntry]: + """ + Lookup the entry with the given code and return it. + + The entries can be looked up by using either the Alpha2 code or the bibliographical + Alpha3 code. + """ + return self.__codeMap.get(code) diff --git a/util/locale_database/qlocalexml2cpp.py b/util/locale_database/qlocalexml2cpp.py index 7ac7945cf8..a7592f7a0c 100755 --- a/util/locale_database/qlocalexml2cpp.py +++ b/util/locale_database/qlocalexml2cpp.py @@ -30,15 +30,22 @@ See ``cldr2qlocalexml.py`` for how to generate the QLocaleXML data itself. Pass the output file from that as first parameter to this script; pass -the root of the qtbase check-out as second parameter. +the ISO 639-3 data file as second parameter; pass the root of the qtbase +check-out as third parameter. + +The ISO 639-3 data file can be downloaded from the SIL website: + + https://iso639-3.sil.org/sites/iso639-3/files/downloads/iso-639-3.tab """ import datetime import argparse from pathlib import Path +from typing import Optional from qlocalexml import QLocaleXmlReader from localetools import unicode2hex, wrap_list, Error, Transcriber, SourceFileEditor +from iso639_3 import LanguageCodeData class LocaleKeySorter: """Sort-ordering representation of a locale key. @@ -389,8 +396,42 @@ class LocaleDataWriter (LocaleSourceEditor): # TODO: unify these next three into the previous three; kept # separate for now to verify we're not changing data. - def languageCodes(self, languages): - self.__writeCodeList(self.writer.write, languages, 'language', 3) + def languageCodes(self, languages, code_data: LanguageCodeData): + out = self.writer.write + + out(f'constexpr std::array<LanguageCodeEntry, {len(languages)}> languageCodeList {{\n') + + def q(val: Optional[str], size: int) -> str: + """Quote the value and adjust the result for tabular view.""" + chars = [] + if val is not None: + for c in val: + chars.append(f"'{c}'") + s = ', '.join(chars) + s = f'{{{s}}}' + else: + s = '' + if size == 0: + return f'{{{s}}}' + else: + return f'{{{s}}},'.ljust(size * 5 + 4) + + for key, value in languages.items(): + code = value[1] + if key < 2: + result = code_data.query('und') + else: + result = code_data.query(code) + assert code == result.id() + assert result is not None + + codeString = q(result.part1Code, 2) + codeString += q(result.part2BCode, 3) + codeString += q(result.part2TCode, 3) + codeString += q(result.part3Code, 0) + out(f' LanguageCodeEntry {{{codeString}}}, // {value[0]}\n') + + out('};\n\n') def scriptCodes(self, scripts): self.__writeCodeList(self.writer.write, scripts, 'script', 4) @@ -519,6 +560,8 @@ def main(out, err): formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('input_file', help='input XML file name', metavar='input-file.xml') + parser.add_argument('iso_path', help='path to the ISO 639-3 data file', + metavar='iso-639-3.tab') parser.add_argument('qtbase_path', help='path to the root of the qtbase source tree') parser.add_argument('--calendars', help='select calendars to emit data for', nargs='+', metavar='CALENDAR', @@ -538,6 +581,8 @@ def main(out, err): locale_map = dict(reader.loadLocaleMap(calendars, err.write)) locale_keys = sorted(locale_map.keys(), key=LocaleKeySorter(reader.defaultMap())) + code_data = LanguageCodeData(args.iso_path) + try: with LocaleDataWriter(qtsrcdir.joinpath('src/corelib/text/qlocale_data_p.h'), qtsrcdir, reader.cldrVersion) as writer: @@ -549,7 +594,7 @@ def main(out, err): writer.scriptNames(reader.scripts) writer.territoryNames(reader.territories) # TODO: merge the next three into the previous three - writer.languageCodes(reader.languages) + writer.languageCodes(reader.languages, code_data) writer.scriptCodes(reader.scripts) writer.territoryCodes(reader.territories) except Exception as e: |