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>
This commit is contained in:
Ievgenii Meshcheriakov 2021-11-22 15:56:53 +01:00
parent 0fbeac0115
commit 4f53c703e4
9 changed files with 724 additions and 362 deletions

View File

@ -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

View File

@ -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 &);

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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: