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" #include "qlocale_data_p.h"
QT_END_INCLUDE_NAMESPACE 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(); const auto len = code.size();
if (len != 2 && len != 3) if (len != 2 && len != 3)
return QLocale::AnyLanguage; 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; const char16_t uc1 = code[0].toLower().unicode();
for (; *c != 0; c += 3) { const char16_t uc2 = code[1].toLower().unicode();
if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2]) const char16_t uc3 = len > 2 ? code[2].toLower().unicode() : 0;
return QLocale::Language((c - language_code_list)/3);
// 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 // legacy codes
if (uc1 == 'n' && uc2 == 'o') // no -> nb if (uc1 == 'n' && uc2 == 'o') // no -> nb
return QLocale::NorwegianBokmal; return QLocale::NorwegianBokmal;
@ -177,15 +213,29 @@ QLocale::Territory QLocalePrivate::codeToTerritory(QStringView code) noexcept
return QLocale::AnyTerritory; 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) if (language == QLocale::AnyLanguage || language > QLocale::LastLanguage)
return QLatin1String(); return QLatin1String();
if (language == QLocale::C) if (language == QLocale::C)
return QLatin1String("C"); return QLatin1String("C");
const unsigned char *c = language_code_list + 3 * language; const LanguageCodeEntry &i = languageCodeList[language];
return QLatin1String(reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3);
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) QLatin1String QLocalePrivate::scriptToCode(QLocale::Script script)
@ -370,20 +420,32 @@ QByteArray QLocaleId::name(char separator) const
if (language_id == QLocale::C) if (language_id == QLocale::C)
return QByteArrayLiteral("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 = const unsigned char *script =
(script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr); (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr);
const unsigned char *country = const unsigned char *country =
(territory_id != QLocale::AnyTerritory (territory_id != QLocale::AnyTerritory
? territory_code_list + 3 * territory_id : nullptr); ? territory_code_list + 3 * territory_id : nullptr);
char len = (lang[2] != 0 ? 3 : 2) + (script ? 4 + 1 : 0) qsizetype len = langLen + (script ? 4 + 1 : 0) + (country ? (country[2] != 0 ? 3 : 2) + 1 : 0);
+ (country ? (country[2] != 0 ? 3 : 2) + 1 : 0);
QByteArray name(len, Qt::Uninitialized); QByteArray name(len, Qt::Uninitialized);
char *uc = name.data(); char *uc = name.data();
*uc++ = lang[0]; *uc++ = lang[0];
*uc++ = lang[1]; *uc++ = lang[1];
if (lang[2] != 0) if (langLen > 2)
*uc++ = lang[2]; *uc++ = lang[2];
if (script) { if (script) {
*uc++ = separator; *uc++ = separator;
*uc++ = script[0]; *uc++ = script[0];
@ -1348,30 +1410,66 @@ QString QLocale::bcp47Name() const
Returns the two- or three-letter language code for \a language, as defined Returns the two- or three-letter language code for \a language, as defined
in the ISO 639 standards. 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"}. \note For \c{QLocale::C} the function returns \c{"C"}.
For \c QLocale::AnyLanguage an empty string is returned. 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() \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) QString QLocale::languageToCode(Language language)
{ {
return QLocalePrivate::languageToCode(language); return QLocalePrivate::languageToCode(language);
} }
#endif
/*! /*!
Returns the QLocale::Language enum corresponding to the two- or three-letter Returns the QLocale::Language enum corresponding to the two- or three-letter
\a languageCode, as defined in the ISO 639 standards. \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() \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 QLocale::Language QLocale::codeToLanguage(QStringView languageCode) noexcept
{ {
return QLocalePrivate::codeToLanguage(languageCode); return QLocalePrivate::codeToLanguage(languageCode);
} }
#endif
/*! /*!
\since 6.2 \since 6.2

View File

@ -1088,8 +1088,32 @@ public:
QStringList uiLanguages() const; 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);
static QString languageToCode(Language language, LanguageCodeTypes codeTypes);
static Language codeToLanguage(QStringView languageCode) noexcept; 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 QString territoryToCode(Territory territory);
static Territory codeToTerritory(QStringView territoryCode) noexcept; static Territory codeToTerritory(QStringView territoryCode) noexcept;
#if QT_DEPRECATED_SINCE(6, 6) #if QT_DEPRECATED_SINCE(6, 6)
@ -1146,6 +1170,7 @@ private:
}; };
Q_DECLARE_SHARED(QLocale) Q_DECLARE_SHARED(QLocale)
Q_DECLARE_OPERATORS_FOR_FLAGS(QLocale::NumberOptions) Q_DECLARE_OPERATORS_FOR_FLAGS(QLocale::NumberOptions)
Q_DECLARE_OPERATORS_FOR_FLAGS(QLocale::LanguageCodeTypes)
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM
Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QLocale &); Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QLocale &);

View File

@ -1033,6 +1033,25 @@
\since 4.4 \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) \fn bool QLocale::operator==(const QLocale &lhs, const QLocale &rhs)

View File

@ -51,6 +51,8 @@
// We mean it. // We mean it.
// //
#include <array>
#include <QtCore/qendian.h>
#include <QtCore/private/qglobal_p.h> #include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -74,10 +76,29 @@ static const TerritoryLanguage ImperialMeasurementSystems[] = {
static const int ImperialMeasurementSystemsCount = static const int ImperialMeasurementSystemsCount =
sizeof(ImperialMeasurementSystems)/sizeof(ImperialMeasurementSystems[0]); 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 // 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 Common Locale Data Repository v40
http://www.unicode.org/cldr/ http://www.unicode.org/cldr/
@ -5337,338 +5358,338 @@ static const quint16 territory_name_index[] = {
2824, // Zimbabwe 2824, // Zimbabwe
}; };
static const unsigned char language_code_list[] = constexpr std::array<LanguageCodeEntry, 330> languageCodeList {
" \0" // AnyLanguage LanguageCodeEntry {{}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}}, // AnyLanguage
" \0" // C LanguageCodeEntry {{}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}, {{'u', 'n', 'd'}}}, // C
"ab\0" // Abkhazian LanguageCodeEntry {{{'a', 'b'}}, {{'a', 'b', 'k'}}, {{'a', 'b', 'k'}}, {{'a', 'b', 'k'}}}, // Abkhazian
"aa\0" // Afar LanguageCodeEntry {{{'a', 'a'}}, {{'a', 'a', 'r'}}, {{'a', 'a', 'r'}}, {{'a', 'a', 'r'}}}, // Afar
"af\0" // Afrikaans LanguageCodeEntry {{{'a', 'f'}}, {{'a', 'f', 'r'}}, {{'a', 'f', 'r'}}, {{'a', 'f', 'r'}}}, // Afrikaans
"agq" // Aghem LanguageCodeEntry {{}, {}, {}, {{'a', 'g', 'q'}}}, // Aghem
"ak\0" // Akan LanguageCodeEntry {{{'a', 'k'}}, {{'a', 'k', 'a'}}, {{'a', 'k', 'a'}}, {{'a', 'k', 'a'}}}, // Akan
"akk" // Akkadian LanguageCodeEntry {{}, {{'a', 'k', 'k'}}, {{'a', 'k', 'k'}}, {{'a', 'k', 'k'}}}, // Akkadian
"bss" // Akoose LanguageCodeEntry {{}, {}, {}, {{'b', 's', 's'}}}, // Akoose
"sq\0" // Albanian LanguageCodeEntry {{{'s', 'q'}}, {{'a', 'l', 'b'}}, {{'s', 'q', 'i'}}, {{'s', 'q', 'i'}}}, // Albanian
"ase" // American Sign Language LanguageCodeEntry {{}, {}, {}, {{'a', 's', 'e'}}}, // American Sign Language
"am\0" // Amharic LanguageCodeEntry {{{'a', 'm'}}, {{'a', 'm', 'h'}}, {{'a', 'm', 'h'}}, {{'a', 'm', 'h'}}}, // Amharic
"egy" // Ancient Egyptian LanguageCodeEntry {{}, {{'e', 'g', 'y'}}, {{'e', 'g', 'y'}}, {{'e', 'g', 'y'}}}, // Ancient Egyptian
"grc" // Ancient Greek LanguageCodeEntry {{}, {{'g', 'r', 'c'}}, {{'g', 'r', 'c'}}, {{'g', 'r', 'c'}}}, // Ancient Greek
"ar\0" // Arabic LanguageCodeEntry {{{'a', 'r'}}, {{'a', 'r', 'a'}}, {{'a', 'r', 'a'}}, {{'a', 'r', 'a'}}}, // Arabic
"an\0" // Aragonese LanguageCodeEntry {{{'a', 'n'}}, {{'a', 'r', 'g'}}, {{'a', 'r', 'g'}}, {{'a', 'r', 'g'}}}, // Aragonese
"arc" // Aramaic LanguageCodeEntry {{}, {{'a', 'r', 'c'}}, {{'a', 'r', 'c'}}, {{'a', 'r', 'c'}}}, // Aramaic
"hy\0" // Armenian LanguageCodeEntry {{{'h', 'y'}}, {{'a', 'r', 'm'}}, {{'h', 'y', 'e'}}, {{'h', 'y', 'e'}}}, // Armenian
"as\0" // Assamese LanguageCodeEntry {{{'a', 's'}}, {{'a', 's', 'm'}}, {{'a', 's', 'm'}}, {{'a', 's', 'm'}}}, // Assamese
"ast" // Asturian LanguageCodeEntry {{}, {{'a', 's', 't'}}, {{'a', 's', 't'}}, {{'a', 's', 't'}}}, // Asturian
"asa" // Asu LanguageCodeEntry {{}, {}, {}, {{'a', 's', 'a'}}}, // Asu
"cch" // Atsam LanguageCodeEntry {{}, {}, {}, {{'c', 'c', 'h'}}}, // Atsam
"av\0" // Avaric LanguageCodeEntry {{{'a', 'v'}}, {{'a', 'v', 'a'}}, {{'a', 'v', 'a'}}, {{'a', 'v', 'a'}}}, // Avaric
"ae\0" // Avestan LanguageCodeEntry {{{'a', 'e'}}, {{'a', 'v', 'e'}}, {{'a', 'v', 'e'}}, {{'a', 'v', 'e'}}}, // Avestan
"ay\0" // Aymara LanguageCodeEntry {{{'a', 'y'}}, {{'a', 'y', 'm'}}, {{'a', 'y', 'm'}}, {{'a', 'y', 'm'}}}, // Aymara
"az\0" // Azerbaijani LanguageCodeEntry {{{'a', 'z'}}, {{'a', 'z', 'e'}}, {{'a', 'z', 'e'}}, {{'a', 'z', 'e'}}}, // Azerbaijani
"ksf" // Bafia LanguageCodeEntry {{}, {}, {}, {{'k', 's', 'f'}}}, // Bafia
"ban" // Balinese LanguageCodeEntry {{}, {{'b', 'a', 'n'}}, {{'b', 'a', 'n'}}, {{'b', 'a', 'n'}}}, // Balinese
"bm\0" // Bambara LanguageCodeEntry {{{'b', 'm'}}, {{'b', 'a', 'm'}}, {{'b', 'a', 'm'}}, {{'b', 'a', 'm'}}}, // Bambara
"bax" // Bamun LanguageCodeEntry {{}, {}, {}, {{'b', 'a', 'x'}}}, // Bamun
"bn\0" // Bangla LanguageCodeEntry {{{'b', 'n'}}, {{'b', 'e', 'n'}}, {{'b', 'e', 'n'}}, {{'b', 'e', 'n'}}}, // Bangla
"bas" // Basaa LanguageCodeEntry {{}, {{'b', 'a', 's'}}, {{'b', 'a', 's'}}, {{'b', 'a', 's'}}}, // Basaa
"ba\0" // Bashkir LanguageCodeEntry {{{'b', 'a'}}, {{'b', 'a', 'k'}}, {{'b', 'a', 'k'}}, {{'b', 'a', 'k'}}}, // Bashkir
"eu\0" // Basque LanguageCodeEntry {{{'e', 'u'}}, {{'b', 'a', 'q'}}, {{'e', 'u', 's'}}, {{'e', 'u', 's'}}}, // Basque
"bbc" // Batak Toba LanguageCodeEntry {{}, {}, {}, {{'b', 'b', 'c'}}}, // Batak Toba
"be\0" // Belarusian LanguageCodeEntry {{{'b', 'e'}}, {{'b', 'e', 'l'}}, {{'b', 'e', 'l'}}, {{'b', 'e', 'l'}}}, // Belarusian
"bem" // Bemba LanguageCodeEntry {{}, {{'b', 'e', 'm'}}, {{'b', 'e', 'm'}}, {{'b', 'e', 'm'}}}, // Bemba
"bez" // Bena LanguageCodeEntry {{}, {}, {}, {{'b', 'e', 'z'}}}, // Bena
"bho" // Bhojpuri LanguageCodeEntry {{}, {{'b', 'h', 'o'}}, {{'b', 'h', 'o'}}, {{'b', 'h', 'o'}}}, // Bhojpuri
"bi\0" // Bislama LanguageCodeEntry {{{'b', 'i'}}, {{'b', 'i', 's'}}, {{'b', 'i', 's'}}, {{'b', 'i', 's'}}}, // Bislama
"byn" // Blin LanguageCodeEntry {{}, {{'b', 'y', 'n'}}, {{'b', 'y', 'n'}}, {{'b', 'y', 'n'}}}, // Blin
"brx" // Bodo LanguageCodeEntry {{}, {}, {}, {{'b', 'r', 'x'}}}, // Bodo
"bs\0" // Bosnian LanguageCodeEntry {{{'b', 's'}}, {{'b', 'o', 's'}}, {{'b', 'o', 's'}}, {{'b', 'o', 's'}}}, // Bosnian
"br\0" // Breton LanguageCodeEntry {{{'b', 'r'}}, {{'b', 'r', 'e'}}, {{'b', 'r', 'e'}}, {{'b', 'r', 'e'}}}, // Breton
"bug" // Buginese LanguageCodeEntry {{}, {{'b', 'u', 'g'}}, {{'b', 'u', 'g'}}, {{'b', 'u', 'g'}}}, // Buginese
"bg\0" // Bulgarian LanguageCodeEntry {{{'b', 'g'}}, {{'b', 'u', 'l'}}, {{'b', 'u', 'l'}}, {{'b', 'u', 'l'}}}, // Bulgarian
"my\0" // Burmese LanguageCodeEntry {{{'m', 'y'}}, {{'b', 'u', 'r'}}, {{'m', 'y', 'a'}}, {{'m', 'y', 'a'}}}, // Burmese
"yue" // Cantonese LanguageCodeEntry {{}, {}, {}, {{'y', 'u', 'e'}}}, // Cantonese
"ca\0" // Catalan LanguageCodeEntry {{{'c', 'a'}}, {{'c', 'a', 't'}}, {{'c', 'a', 't'}}, {{'c', 'a', 't'}}}, // Catalan
"ceb" // Cebuano LanguageCodeEntry {{}, {{'c', 'e', 'b'}}, {{'c', 'e', 'b'}}, {{'c', 'e', 'b'}}}, // Cebuano
"tzm" // Central Atlas Tamazight LanguageCodeEntry {{}, {}, {}, {{'t', 'z', 'm'}}}, // Central Atlas Tamazight
"ckb" // Central Kurdish LanguageCodeEntry {{}, {}, {}, {{'c', 'k', 'b'}}}, // Central Kurdish
"ccp" // Chakma LanguageCodeEntry {{}, {}, {}, {{'c', 'c', 'p'}}}, // Chakma
"ch\0" // Chamorro LanguageCodeEntry {{{'c', 'h'}}, {{'c', 'h', 'a'}}, {{'c', 'h', 'a'}}, {{'c', 'h', 'a'}}}, // Chamorro
"ce\0" // Chechen LanguageCodeEntry {{{'c', 'e'}}, {{'c', 'h', 'e'}}, {{'c', 'h', 'e'}}, {{'c', 'h', 'e'}}}, // Chechen
"chr" // Cherokee LanguageCodeEntry {{}, {{'c', 'h', 'r'}}, {{'c', 'h', 'r'}}, {{'c', 'h', 'r'}}}, // Cherokee
"cic" // Chickasaw LanguageCodeEntry {{}, {}, {}, {{'c', 'i', 'c'}}}, // Chickasaw
"cgg" // Chiga LanguageCodeEntry {{}, {}, {}, {{'c', 'g', 'g'}}}, // Chiga
"zh\0" // Chinese LanguageCodeEntry {{{'z', 'h'}}, {{'c', 'h', 'i'}}, {{'z', 'h', 'o'}}, {{'z', 'h', 'o'}}}, // Chinese
"cu\0" // Church LanguageCodeEntry {{{'c', 'u'}}, {{'c', 'h', 'u'}}, {{'c', 'h', 'u'}}, {{'c', 'h', 'u'}}}, // Church
"cv\0" // Chuvash LanguageCodeEntry {{{'c', 'v'}}, {{'c', 'h', 'v'}}, {{'c', 'h', 'v'}}, {{'c', 'h', 'v'}}}, // Chuvash
"ksh" // Colognian LanguageCodeEntry {{}, {}, {}, {{'k', 's', 'h'}}}, // Colognian
"cop" // Coptic LanguageCodeEntry {{}, {{'c', 'o', 'p'}}, {{'c', 'o', 'p'}}, {{'c', 'o', 'p'}}}, // Coptic
"kw\0" // Cornish LanguageCodeEntry {{{'k', 'w'}}, {{'c', 'o', 'r'}}, {{'c', 'o', 'r'}}, {{'c', 'o', 'r'}}}, // Cornish
"co\0" // Corsican LanguageCodeEntry {{{'c', 'o'}}, {{'c', 'o', 's'}}, {{'c', 'o', 's'}}, {{'c', 'o', 's'}}}, // Corsican
"cr\0" // Cree LanguageCodeEntry {{{'c', 'r'}}, {{'c', 'r', 'e'}}, {{'c', 'r', 'e'}}, {{'c', 'r', 'e'}}}, // Cree
"hr\0" // Croatian LanguageCodeEntry {{{'h', 'r'}}, {{'h', 'r', 'v'}}, {{'h', 'r', 'v'}}, {{'h', 'r', 'v'}}}, // Croatian
"cs\0" // Czech LanguageCodeEntry {{{'c', 's'}}, {{'c', 'z', 'e'}}, {{'c', 'e', 's'}}, {{'c', 'e', 's'}}}, // Czech
"da\0" // Danish LanguageCodeEntry {{{'d', 'a'}}, {{'d', 'a', 'n'}}, {{'d', 'a', 'n'}}, {{'d', 'a', 'n'}}}, // Danish
"dv\0" // Divehi LanguageCodeEntry {{{'d', 'v'}}, {{'d', 'i', 'v'}}, {{'d', 'i', 'v'}}, {{'d', 'i', 'v'}}}, // Divehi
"doi" // Dogri LanguageCodeEntry {{}, {{'d', 'o', 'i'}}, {{'d', 'o', 'i'}}, {{'d', 'o', 'i'}}}, // Dogri
"dua" // Duala LanguageCodeEntry {{}, {{'d', 'u', 'a'}}, {{'d', 'u', 'a'}}, {{'d', 'u', 'a'}}}, // Duala
"nl\0" // Dutch LanguageCodeEntry {{{'n', 'l'}}, {{'d', 'u', 't'}}, {{'n', 'l', 'd'}}, {{'n', 'l', 'd'}}}, // Dutch
"dz\0" // Dzongkha LanguageCodeEntry {{{'d', 'z'}}, {{'d', 'z', 'o'}}, {{'d', 'z', 'o'}}, {{'d', 'z', 'o'}}}, // Dzongkha
"ebu" // Embu LanguageCodeEntry {{}, {}, {}, {{'e', 'b', 'u'}}}, // Embu
"en\0" // English LanguageCodeEntry {{{'e', 'n'}}, {{'e', 'n', 'g'}}, {{'e', 'n', 'g'}}, {{'e', 'n', 'g'}}}, // English
"myv" // Erzya LanguageCodeEntry {{}, {{'m', 'y', 'v'}}, {{'m', 'y', 'v'}}, {{'m', 'y', 'v'}}}, // Erzya
"eo\0" // Esperanto LanguageCodeEntry {{{'e', 'o'}}, {{'e', 'p', 'o'}}, {{'e', 'p', 'o'}}, {{'e', 'p', 'o'}}}, // Esperanto
"et\0" // Estonian LanguageCodeEntry {{{'e', 't'}}, {{'e', 's', 't'}}, {{'e', 's', 't'}}, {{'e', 's', 't'}}}, // Estonian
"ee\0" // Ewe LanguageCodeEntry {{{'e', 'e'}}, {{'e', 'w', 'e'}}, {{'e', 'w', 'e'}}, {{'e', 'w', 'e'}}}, // Ewe
"ewo" // Ewondo LanguageCodeEntry {{}, {{'e', 'w', 'o'}}, {{'e', 'w', 'o'}}, {{'e', 'w', 'o'}}}, // Ewondo
"fo\0" // Faroese LanguageCodeEntry {{{'f', 'o'}}, {{'f', 'a', 'o'}}, {{'f', 'a', 'o'}}, {{'f', 'a', 'o'}}}, // Faroese
"fj\0" // Fijian LanguageCodeEntry {{{'f', 'j'}}, {{'f', 'i', 'j'}}, {{'f', 'i', 'j'}}, {{'f', 'i', 'j'}}}, // Fijian
"fil" // Filipino LanguageCodeEntry {{}, {{'f', 'i', 'l'}}, {{'f', 'i', 'l'}}, {{'f', 'i', 'l'}}}, // Filipino
"fi\0" // Finnish LanguageCodeEntry {{{'f', 'i'}}, {{'f', 'i', 'n'}}, {{'f', 'i', 'n'}}, {{'f', 'i', 'n'}}}, // Finnish
"fr\0" // French LanguageCodeEntry {{{'f', 'r'}}, {{'f', 'r', 'e'}}, {{'f', 'r', 'a'}}, {{'f', 'r', 'a'}}}, // French
"fur" // Friulian LanguageCodeEntry {{}, {{'f', 'u', 'r'}}, {{'f', 'u', 'r'}}, {{'f', 'u', 'r'}}}, // Friulian
"ff\0" // Fulah LanguageCodeEntry {{{'f', 'f'}}, {{'f', 'u', 'l'}}, {{'f', 'u', 'l'}}, {{'f', 'u', 'l'}}}, // Fulah
"gd\0" // Gaelic LanguageCodeEntry {{{'g', 'd'}}, {{'g', 'l', 'a'}}, {{'g', 'l', 'a'}}, {{'g', 'l', 'a'}}}, // Gaelic
"gaa" // Ga LanguageCodeEntry {{}, {{'g', 'a', 'a'}}, {{'g', 'a', 'a'}}, {{'g', 'a', 'a'}}}, // Ga
"gl\0" // Galician LanguageCodeEntry {{{'g', 'l'}}, {{'g', 'l', 'g'}}, {{'g', 'l', 'g'}}, {{'g', 'l', 'g'}}}, // Galician
"lg\0" // Ganda LanguageCodeEntry {{{'l', 'g'}}, {{'l', 'u', 'g'}}, {{'l', 'u', 'g'}}, {{'l', 'u', 'g'}}}, // Ganda
"gez" // Geez LanguageCodeEntry {{}, {{'g', 'e', 'z'}}, {{'g', 'e', 'z'}}, {{'g', 'e', 'z'}}}, // Geez
"ka\0" // Georgian LanguageCodeEntry {{{'k', 'a'}}, {{'g', 'e', 'o'}}, {{'k', 'a', 't'}}, {{'k', 'a', 't'}}}, // Georgian
"de\0" // German LanguageCodeEntry {{{'d', 'e'}}, {{'g', 'e', 'r'}}, {{'d', 'e', 'u'}}, {{'d', 'e', 'u'}}}, // German
"got" // Gothic LanguageCodeEntry {{}, {{'g', 'o', 't'}}, {{'g', 'o', 't'}}, {{'g', 'o', 't'}}}, // Gothic
"el\0" // Greek LanguageCodeEntry {{{'e', 'l'}}, {{'g', 'r', 'e'}}, {{'e', 'l', 'l'}}, {{'e', 'l', 'l'}}}, // Greek
"gn\0" // Guarani LanguageCodeEntry {{{'g', 'n'}}, {{'g', 'r', 'n'}}, {{'g', 'r', 'n'}}, {{'g', 'r', 'n'}}}, // Guarani
"gu\0" // Gujarati LanguageCodeEntry {{{'g', 'u'}}, {{'g', 'u', 'j'}}, {{'g', 'u', 'j'}}, {{'g', 'u', 'j'}}}, // Gujarati
"guz" // Gusii LanguageCodeEntry {{}, {}, {}, {{'g', 'u', 'z'}}}, // Gusii
"ht\0" // Haitian LanguageCodeEntry {{{'h', 't'}}, {{'h', 'a', 't'}}, {{'h', 'a', 't'}}, {{'h', 'a', 't'}}}, // Haitian
"ha\0" // Hausa LanguageCodeEntry {{{'h', 'a'}}, {{'h', 'a', 'u'}}, {{'h', 'a', 'u'}}, {{'h', 'a', 'u'}}}, // Hausa
"haw" // Hawaiian LanguageCodeEntry {{}, {{'h', 'a', 'w'}}, {{'h', 'a', 'w'}}, {{'h', 'a', 'w'}}}, // Hawaiian
"he\0" // Hebrew LanguageCodeEntry {{{'h', 'e'}}, {{'h', 'e', 'b'}}, {{'h', 'e', 'b'}}, {{'h', 'e', 'b'}}}, // Hebrew
"hz\0" // Herero LanguageCodeEntry {{{'h', 'z'}}, {{'h', 'e', 'r'}}, {{'h', 'e', 'r'}}, {{'h', 'e', 'r'}}}, // Herero
"hi\0" // Hindi LanguageCodeEntry {{{'h', 'i'}}, {{'h', 'i', 'n'}}, {{'h', 'i', 'n'}}, {{'h', 'i', 'n'}}}, // Hindi
"ho\0" // Hiri Motu LanguageCodeEntry {{{'h', 'o'}}, {{'h', 'm', 'o'}}, {{'h', 'm', 'o'}}, {{'h', 'm', 'o'}}}, // Hiri Motu
"hu\0" // Hungarian LanguageCodeEntry {{{'h', 'u'}}, {{'h', 'u', 'n'}}, {{'h', 'u', 'n'}}, {{'h', 'u', 'n'}}}, // Hungarian
"is\0" // Icelandic LanguageCodeEntry {{{'i', 's'}}, {{'i', 'c', 'e'}}, {{'i', 's', 'l'}}, {{'i', 's', 'l'}}}, // Icelandic
"io\0" // Ido LanguageCodeEntry {{{'i', 'o'}}, {{'i', 'd', 'o'}}, {{'i', 'd', 'o'}}, {{'i', 'd', 'o'}}}, // Ido
"ig\0" // Igbo LanguageCodeEntry {{{'i', 'g'}}, {{'i', 'b', 'o'}}, {{'i', 'b', 'o'}}, {{'i', 'b', 'o'}}}, // Igbo
"smn" // Inari Sami LanguageCodeEntry {{}, {{'s', 'm', 'n'}}, {{'s', 'm', 'n'}}, {{'s', 'm', 'n'}}}, // Inari Sami
"id\0" // Indonesian LanguageCodeEntry {{{'i', 'd'}}, {{'i', 'n', 'd'}}, {{'i', 'n', 'd'}}, {{'i', 'n', 'd'}}}, // Indonesian
"inh" // Ingush LanguageCodeEntry {{}, {{'i', 'n', 'h'}}, {{'i', 'n', 'h'}}, {{'i', 'n', 'h'}}}, // Ingush
"ia\0" // Interlingua LanguageCodeEntry {{{'i', 'a'}}, {{'i', 'n', 'a'}}, {{'i', 'n', 'a'}}, {{'i', 'n', 'a'}}}, // Interlingua
"ie\0" // Interlingue LanguageCodeEntry {{{'i', 'e'}}, {{'i', 'l', 'e'}}, {{'i', 'l', 'e'}}, {{'i', 'l', 'e'}}}, // Interlingue
"iu\0" // Inuktitut LanguageCodeEntry {{{'i', 'u'}}, {{'i', 'k', 'u'}}, {{'i', 'k', 'u'}}, {{'i', 'k', 'u'}}}, // Inuktitut
"ik\0" // Inupiaq LanguageCodeEntry {{{'i', 'k'}}, {{'i', 'p', 'k'}}, {{'i', 'p', 'k'}}, {{'i', 'p', 'k'}}}, // Inupiaq
"ga\0" // Irish LanguageCodeEntry {{{'g', 'a'}}, {{'g', 'l', 'e'}}, {{'g', 'l', 'e'}}, {{'g', 'l', 'e'}}}, // Irish
"it\0" // Italian LanguageCodeEntry {{{'i', 't'}}, {{'i', 't', 'a'}}, {{'i', 't', 'a'}}, {{'i', 't', 'a'}}}, // Italian
"ja\0" // Japanese LanguageCodeEntry {{{'j', 'a'}}, {{'j', 'p', 'n'}}, {{'j', 'p', 'n'}}, {{'j', 'p', 'n'}}}, // Japanese
"jv\0" // Javanese LanguageCodeEntry {{{'j', 'v'}}, {{'j', 'a', 'v'}}, {{'j', 'a', 'v'}}, {{'j', 'a', 'v'}}}, // Javanese
"kaj" // Jju LanguageCodeEntry {{}, {}, {}, {{'k', 'a', 'j'}}}, // Jju
"dyo" // Jola Fonyi LanguageCodeEntry {{}, {}, {}, {{'d', 'y', 'o'}}}, // Jola Fonyi
"kea" // Kabuverdianu LanguageCodeEntry {{}, {}, {}, {{'k', 'e', 'a'}}}, // Kabuverdianu
"kab" // Kabyle LanguageCodeEntry {{}, {{'k', 'a', 'b'}}, {{'k', 'a', 'b'}}, {{'k', 'a', 'b'}}}, // Kabyle
"kkj" // Kako LanguageCodeEntry {{}, {}, {}, {{'k', 'k', 'j'}}}, // Kako
"kl\0" // Kalaallisut LanguageCodeEntry {{{'k', 'l'}}, {{'k', 'a', 'l'}}, {{'k', 'a', 'l'}}, {{'k', 'a', 'l'}}}, // Kalaallisut
"kln" // Kalenjin LanguageCodeEntry {{}, {}, {}, {{'k', 'l', 'n'}}}, // Kalenjin
"kam" // Kamba LanguageCodeEntry {{}, {{'k', 'a', 'm'}}, {{'k', 'a', 'm'}}, {{'k', 'a', 'm'}}}, // Kamba
"kn\0" // Kannada LanguageCodeEntry {{{'k', 'n'}}, {{'k', 'a', 'n'}}, {{'k', 'a', 'n'}}, {{'k', 'a', 'n'}}}, // Kannada
"kr\0" // Kanuri LanguageCodeEntry {{{'k', 'r'}}, {{'k', 'a', 'u'}}, {{'k', 'a', 'u'}}, {{'k', 'a', 'u'}}}, // Kanuri
"ks\0" // Kashmiri LanguageCodeEntry {{{'k', 's'}}, {{'k', 'a', 's'}}, {{'k', 'a', 's'}}, {{'k', 'a', 's'}}}, // Kashmiri
"kk\0" // Kazakh LanguageCodeEntry {{{'k', 'k'}}, {{'k', 'a', 'z'}}, {{'k', 'a', 'z'}}, {{'k', 'a', 'z'}}}, // Kazakh
"ken" // Kenyang LanguageCodeEntry {{}, {}, {}, {{'k', 'e', 'n'}}}, // Kenyang
"km\0" // Khmer LanguageCodeEntry {{{'k', 'm'}}, {{'k', 'h', 'm'}}, {{'k', 'h', 'm'}}, {{'k', 'h', 'm'}}}, // Khmer
"quc" // Kiche LanguageCodeEntry {{}, {}, {}, {{'q', 'u', 'c'}}}, // Kiche
"ki\0" // Kikuyu LanguageCodeEntry {{{'k', 'i'}}, {{'k', 'i', 'k'}}, {{'k', 'i', 'k'}}, {{'k', 'i', 'k'}}}, // Kikuyu
"rw\0" // Kinyarwanda LanguageCodeEntry {{{'r', 'w'}}, {{'k', 'i', 'n'}}, {{'k', 'i', 'n'}}, {{'k', 'i', 'n'}}}, // Kinyarwanda
"kv\0" // Komi LanguageCodeEntry {{{'k', 'v'}}, {{'k', 'o', 'm'}}, {{'k', 'o', 'm'}}, {{'k', 'o', 'm'}}}, // Komi
"kg\0" // Kongo LanguageCodeEntry {{{'k', 'g'}}, {{'k', 'o', 'n'}}, {{'k', 'o', 'n'}}, {{'k', 'o', 'n'}}}, // Kongo
"kok" // Konkani LanguageCodeEntry {{}, {{'k', 'o', 'k'}}, {{'k', 'o', 'k'}}, {{'k', 'o', 'k'}}}, // Konkani
"ko\0" // Korean LanguageCodeEntry {{{'k', 'o'}}, {{'k', 'o', 'r'}}, {{'k', 'o', 'r'}}, {{'k', 'o', 'r'}}}, // Korean
"kfo" // Koro LanguageCodeEntry {{}, {}, {}, {{'k', 'f', 'o'}}}, // Koro
"ses" // Koyraboro Senni LanguageCodeEntry {{}, {}, {}, {{'s', 'e', 's'}}}, // Koyraboro Senni
"khq" // Koyra Chiini LanguageCodeEntry {{}, {}, {}, {{'k', 'h', 'q'}}}, // Koyra Chiini
"kpe" // Kpelle LanguageCodeEntry {{}, {{'k', 'p', 'e'}}, {{'k', 'p', 'e'}}, {{'k', 'p', 'e'}}}, // Kpelle
"kj\0" // Kuanyama LanguageCodeEntry {{{'k', 'j'}}, {{'k', 'u', 'a'}}, {{'k', 'u', 'a'}}, {{'k', 'u', 'a'}}}, // Kuanyama
"ku\0" // Kurdish LanguageCodeEntry {{{'k', 'u'}}, {{'k', 'u', 'r'}}, {{'k', 'u', 'r'}}, {{'k', 'u', 'r'}}}, // Kurdish
"nmg" // Kwasio LanguageCodeEntry {{}, {}, {}, {{'n', 'm', 'g'}}}, // Kwasio
"ky\0" // Kyrgyz LanguageCodeEntry {{{'k', 'y'}}, {{'k', 'i', 'r'}}, {{'k', 'i', 'r'}}, {{'k', 'i', 'r'}}}, // Kyrgyz
"lkt" // Lakota LanguageCodeEntry {{}, {}, {}, {{'l', 'k', 't'}}}, // Lakota
"lag" // Langi LanguageCodeEntry {{}, {}, {}, {{'l', 'a', 'g'}}}, // Langi
"lo\0" // Lao LanguageCodeEntry {{{'l', 'o'}}, {{'l', 'a', 'o'}}, {{'l', 'a', 'o'}}, {{'l', 'a', 'o'}}}, // Lao
"la\0" // Latin LanguageCodeEntry {{{'l', 'a'}}, {{'l', 'a', 't'}}, {{'l', 'a', 't'}}, {{'l', 'a', 't'}}}, // Latin
"lv\0" // Latvian LanguageCodeEntry {{{'l', 'v'}}, {{'l', 'a', 'v'}}, {{'l', 'a', 'v'}}, {{'l', 'a', 'v'}}}, // Latvian
"lez" // Lezghian LanguageCodeEntry {{}, {{'l', 'e', 'z'}}, {{'l', 'e', 'z'}}, {{'l', 'e', 'z'}}}, // Lezghian
"li\0" // Limburgish LanguageCodeEntry {{{'l', 'i'}}, {{'l', 'i', 'm'}}, {{'l', 'i', 'm'}}, {{'l', 'i', 'm'}}}, // Limburgish
"ln\0" // Lingala LanguageCodeEntry {{{'l', 'n'}}, {{'l', 'i', 'n'}}, {{'l', 'i', 'n'}}, {{'l', 'i', 'n'}}}, // Lingala
"lzh" // Literary Chinese LanguageCodeEntry {{}, {}, {}, {{'l', 'z', 'h'}}}, // Literary Chinese
"lt\0" // Lithuanian LanguageCodeEntry {{{'l', 't'}}, {{'l', 'i', 't'}}, {{'l', 'i', 't'}}, {{'l', 'i', 't'}}}, // Lithuanian
"jbo" // Lojban LanguageCodeEntry {{}, {{'j', 'b', 'o'}}, {{'j', 'b', 'o'}}, {{'j', 'b', 'o'}}}, // Lojban
"dsb" // Lower Sorbian LanguageCodeEntry {{}, {{'d', 's', 'b'}}, {{'d', 's', 'b'}}, {{'d', 's', 'b'}}}, // Lower Sorbian
"nds" // Low German LanguageCodeEntry {{}, {{'n', 'd', 's'}}, {{'n', 'd', 's'}}, {{'n', 'd', 's'}}}, // Low German
"lu\0" // Luba Katanga LanguageCodeEntry {{{'l', 'u'}}, {{'l', 'u', 'b'}}, {{'l', 'u', 'b'}}, {{'l', 'u', 'b'}}}, // Luba Katanga
"smj" // Lule Sami LanguageCodeEntry {{}, {{'s', 'm', 'j'}}, {{'s', 'm', 'j'}}, {{'s', 'm', 'j'}}}, // Lule Sami
"luo" // Luo LanguageCodeEntry {{}, {{'l', 'u', 'o'}}, {{'l', 'u', 'o'}}, {{'l', 'u', 'o'}}}, // Luo
"lb\0" // Luxembourgish LanguageCodeEntry {{{'l', 'b'}}, {{'l', 't', 'z'}}, {{'l', 't', 'z'}}, {{'l', 't', 'z'}}}, // Luxembourgish
"luy" // Luyia LanguageCodeEntry {{}, {}, {}, {{'l', 'u', 'y'}}}, // Luyia
"mk\0" // Macedonian LanguageCodeEntry {{{'m', 'k'}}, {{'m', 'a', 'c'}}, {{'m', 'k', 'd'}}, {{'m', 'k', 'd'}}}, // Macedonian
"jmc" // Machame LanguageCodeEntry {{}, {}, {}, {{'j', 'm', 'c'}}}, // Machame
"mai" // Maithili LanguageCodeEntry {{}, {{'m', 'a', 'i'}}, {{'m', 'a', 'i'}}, {{'m', 'a', 'i'}}}, // Maithili
"mgh" // Makhuwa Meetto LanguageCodeEntry {{}, {}, {}, {{'m', 'g', 'h'}}}, // Makhuwa Meetto
"kde" // Makonde LanguageCodeEntry {{}, {}, {}, {{'k', 'd', 'e'}}}, // Makonde
"mg\0" // Malagasy LanguageCodeEntry {{{'m', 'g'}}, {{'m', 'l', 'g'}}, {{'m', 'l', 'g'}}, {{'m', 'l', 'g'}}}, // Malagasy
"ml\0" // Malayalam LanguageCodeEntry {{{'m', 'l'}}, {{'m', 'a', 'l'}}, {{'m', 'a', 'l'}}, {{'m', 'a', 'l'}}}, // Malayalam
"ms\0" // Malay LanguageCodeEntry {{{'m', 's'}}, {{'m', 'a', 'y'}}, {{'m', 's', 'a'}}, {{'m', 's', 'a'}}}, // Malay
"mt\0" // Maltese LanguageCodeEntry {{{'m', 't'}}, {{'m', 'l', 't'}}, {{'m', 'l', 't'}}, {{'m', 'l', 't'}}}, // Maltese
"man" // Mandingo LanguageCodeEntry {{}, {{'m', 'a', 'n'}}, {{'m', 'a', 'n'}}, {{'m', 'a', 'n'}}}, // Mandingo
"mni" // Manipuri LanguageCodeEntry {{}, {{'m', 'n', 'i'}}, {{'m', 'n', 'i'}}, {{'m', 'n', 'i'}}}, // Manipuri
"gv\0" // Manx LanguageCodeEntry {{{'g', 'v'}}, {{'g', 'l', 'v'}}, {{'g', 'l', 'v'}}, {{'g', 'l', 'v'}}}, // Manx
"mi\0" // Maori LanguageCodeEntry {{{'m', 'i'}}, {{'m', 'a', 'o'}}, {{'m', 'r', 'i'}}, {{'m', 'r', 'i'}}}, // Maori
"arn" // Mapuche LanguageCodeEntry {{}, {{'a', 'r', 'n'}}, {{'a', 'r', 'n'}}, {{'a', 'r', 'n'}}}, // Mapuche
"mr\0" // Marathi LanguageCodeEntry {{{'m', 'r'}}, {{'m', 'a', 'r'}}, {{'m', 'a', 'r'}}, {{'m', 'a', 'r'}}}, // Marathi
"mh\0" // Marshallese LanguageCodeEntry {{{'m', 'h'}}, {{'m', 'a', 'h'}}, {{'m', 'a', 'h'}}, {{'m', 'a', 'h'}}}, // Marshallese
"mas" // Masai LanguageCodeEntry {{}, {{'m', 'a', 's'}}, {{'m', 'a', 's'}}, {{'m', 'a', 's'}}}, // Masai
"mzn" // Mazanderani LanguageCodeEntry {{}, {}, {}, {{'m', 'z', 'n'}}}, // Mazanderani
"men" // Mende LanguageCodeEntry {{}, {{'m', 'e', 'n'}}, {{'m', 'e', 'n'}}, {{'m', 'e', 'n'}}}, // Mende
"mer" // Meru LanguageCodeEntry {{}, {}, {}, {{'m', 'e', 'r'}}}, // Meru
"mgo" // Meta LanguageCodeEntry {{}, {}, {}, {{'m', 'g', 'o'}}}, // Meta
"moh" // Mohawk LanguageCodeEntry {{}, {{'m', 'o', 'h'}}, {{'m', 'o', 'h'}}, {{'m', 'o', 'h'}}}, // Mohawk
"mn\0" // Mongolian LanguageCodeEntry {{{'m', 'n'}}, {{'m', 'o', 'n'}}, {{'m', 'o', 'n'}}, {{'m', 'o', 'n'}}}, // Mongolian
"mfe" // Morisyen LanguageCodeEntry {{}, {}, {}, {{'m', 'f', 'e'}}}, // Morisyen
"mua" // Mundang LanguageCodeEntry {{}, {}, {}, {{'m', 'u', 'a'}}}, // Mundang
"mus" // Muscogee LanguageCodeEntry {{}, {{'m', 'u', 's'}}, {{'m', 'u', 's'}}, {{'m', 'u', 's'}}}, // Muscogee
"naq" // Nama LanguageCodeEntry {{}, {}, {}, {{'n', 'a', 'q'}}}, // Nama
"na\0" // Nauru LanguageCodeEntry {{{'n', 'a'}}, {{'n', 'a', 'u'}}, {{'n', 'a', 'u'}}, {{'n', 'a', 'u'}}}, // Nauru
"nv\0" // Navajo LanguageCodeEntry {{{'n', 'v'}}, {{'n', 'a', 'v'}}, {{'n', 'a', 'v'}}, {{'n', 'a', 'v'}}}, // Navajo
"ng\0" // Ndonga LanguageCodeEntry {{{'n', 'g'}}, {{'n', 'd', 'o'}}, {{'n', 'd', 'o'}}, {{'n', 'd', 'o'}}}, // Ndonga
"ne\0" // Nepali LanguageCodeEntry {{{'n', 'e'}}, {{'n', 'e', 'p'}}, {{'n', 'e', 'p'}}, {{'n', 'e', 'p'}}}, // Nepali
"new" // Newari LanguageCodeEntry {{}, {{'n', 'e', 'w'}}, {{'n', 'e', 'w'}}, {{'n', 'e', 'w'}}}, // Newari
"nnh" // Ngiemboon LanguageCodeEntry {{}, {}, {}, {{'n', 'n', 'h'}}}, // Ngiemboon
"jgo" // Ngomba LanguageCodeEntry {{}, {}, {}, {{'j', 'g', 'o'}}}, // Ngomba
"pcm" // Nigerian Pidgin LanguageCodeEntry {{}, {}, {}, {{'p', 'c', 'm'}}}, // Nigerian Pidgin
"nqo" // Nko LanguageCodeEntry {{}, {{'n', 'q', 'o'}}, {{'n', 'q', 'o'}}, {{'n', 'q', 'o'}}}, // Nko
"lrc" // Northern Luri LanguageCodeEntry {{}, {}, {}, {{'l', 'r', 'c'}}}, // Northern Luri
"se\0" // Northern Sami LanguageCodeEntry {{{'s', 'e'}}, {{'s', 'm', 'e'}}, {{'s', 'm', 'e'}}, {{'s', 'm', 'e'}}}, // Northern Sami
"nso" // Northern Sotho LanguageCodeEntry {{}, {{'n', 's', 'o'}}, {{'n', 's', 'o'}}, {{'n', 's', 'o'}}}, // Northern Sotho
"nd\0" // North Ndebele LanguageCodeEntry {{{'n', 'd'}}, {{'n', 'd', 'e'}}, {{'n', 'd', 'e'}}, {{'n', 'd', 'e'}}}, // North Ndebele
"nb\0" // Norwegian Bokmal LanguageCodeEntry {{{'n', 'b'}}, {{'n', 'o', 'b'}}, {{'n', 'o', 'b'}}, {{'n', 'o', 'b'}}}, // Norwegian Bokmal
"nn\0" // Norwegian Nynorsk LanguageCodeEntry {{{'n', 'n'}}, {{'n', 'n', 'o'}}, {{'n', 'n', 'o'}}, {{'n', 'n', 'o'}}}, // Norwegian Nynorsk
"nus" // Nuer LanguageCodeEntry {{}, {}, {}, {{'n', 'u', 's'}}}, // Nuer
"ny\0" // Nyanja LanguageCodeEntry {{{'n', 'y'}}, {{'n', 'y', 'a'}}, {{'n', 'y', 'a'}}, {{'n', 'y', 'a'}}}, // Nyanja
"nyn" // Nyankole LanguageCodeEntry {{}, {{'n', 'y', 'n'}}, {{'n', 'y', 'n'}}, {{'n', 'y', 'n'}}}, // Nyankole
"oc\0" // Occitan LanguageCodeEntry {{{'o', 'c'}}, {{'o', 'c', 'i'}}, {{'o', 'c', 'i'}}, {{'o', 'c', 'i'}}}, // Occitan
"or\0" // Odia LanguageCodeEntry {{{'o', 'r'}}, {{'o', 'r', 'i'}}, {{'o', 'r', 'i'}}, {{'o', 'r', 'i'}}}, // Odia
"oj\0" // Ojibwa LanguageCodeEntry {{{'o', 'j'}}, {{'o', 'j', 'i'}}, {{'o', 'j', 'i'}}, {{'o', 'j', 'i'}}}, // Ojibwa
"sga" // Old Irish LanguageCodeEntry {{}, {{'s', 'g', 'a'}}, {{'s', 'g', 'a'}}, {{'s', 'g', 'a'}}}, // Old Irish
"non" // Old Norse LanguageCodeEntry {{}, {{'n', 'o', 'n'}}, {{'n', 'o', 'n'}}, {{'n', 'o', 'n'}}}, // Old Norse
"peo" // Old Persian LanguageCodeEntry {{}, {{'p', 'e', 'o'}}, {{'p', 'e', 'o'}}, {{'p', 'e', 'o'}}}, // Old Persian
"om\0" // Oromo LanguageCodeEntry {{{'o', 'm'}}, {{'o', 'r', 'm'}}, {{'o', 'r', 'm'}}, {{'o', 'r', 'm'}}}, // Oromo
"osa" // Osage LanguageCodeEntry {{}, {{'o', 's', 'a'}}, {{'o', 's', 'a'}}, {{'o', 's', 'a'}}}, // Osage
"os\0" // Ossetic LanguageCodeEntry {{{'o', 's'}}, {{'o', 's', 's'}}, {{'o', 's', 's'}}, {{'o', 's', 's'}}}, // Ossetic
"pal" // Pahlavi LanguageCodeEntry {{}, {{'p', 'a', 'l'}}, {{'p', 'a', 'l'}}, {{'p', 'a', 'l'}}}, // Pahlavi
"pau" // Palauan LanguageCodeEntry {{}, {{'p', 'a', 'u'}}, {{'p', 'a', 'u'}}, {{'p', 'a', 'u'}}}, // Palauan
"pi\0" // Pali LanguageCodeEntry {{{'p', 'i'}}, {{'p', 'l', 'i'}}, {{'p', 'l', 'i'}}, {{'p', 'l', 'i'}}}, // Pali
"pap" // Papiamento LanguageCodeEntry {{}, {{'p', 'a', 'p'}}, {{'p', 'a', 'p'}}, {{'p', 'a', 'p'}}}, // Papiamento
"ps\0" // Pashto LanguageCodeEntry {{{'p', 's'}}, {{'p', 'u', 's'}}, {{'p', 'u', 's'}}, {{'p', 'u', 's'}}}, // Pashto
"fa\0" // Persian LanguageCodeEntry {{{'f', 'a'}}, {{'p', 'e', 'r'}}, {{'f', 'a', 's'}}, {{'f', 'a', 's'}}}, // Persian
"phn" // Phoenician LanguageCodeEntry {{}, {{'p', 'h', 'n'}}, {{'p', 'h', 'n'}}, {{'p', 'h', 'n'}}}, // Phoenician
"pl\0" // Polish LanguageCodeEntry {{{'p', 'l'}}, {{'p', 'o', 'l'}}, {{'p', 'o', 'l'}}, {{'p', 'o', 'l'}}}, // Polish
"pt\0" // Portuguese LanguageCodeEntry {{{'p', 't'}}, {{'p', 'o', 'r'}}, {{'p', 'o', 'r'}}, {{'p', 'o', 'r'}}}, // Portuguese
"prg" // Prussian LanguageCodeEntry {{}, {}, {}, {{'p', 'r', 'g'}}}, // Prussian
"pa\0" // Punjabi LanguageCodeEntry {{{'p', 'a'}}, {{'p', 'a', 'n'}}, {{'p', 'a', 'n'}}, {{'p', 'a', 'n'}}}, // Punjabi
"qu\0" // Quechua LanguageCodeEntry {{{'q', 'u'}}, {{'q', 'u', 'e'}}, {{'q', 'u', 'e'}}, {{'q', 'u', 'e'}}}, // Quechua
"ro\0" // Romanian LanguageCodeEntry {{{'r', 'o'}}, {{'r', 'u', 'm'}}, {{'r', 'o', 'n'}}, {{'r', 'o', 'n'}}}, // Romanian
"rm\0" // Romansh LanguageCodeEntry {{{'r', 'm'}}, {{'r', 'o', 'h'}}, {{'r', 'o', 'h'}}, {{'r', 'o', 'h'}}}, // Romansh
"rof" // Rombo LanguageCodeEntry {{}, {}, {}, {{'r', 'o', 'f'}}}, // Rombo
"rn\0" // Rundi LanguageCodeEntry {{{'r', 'n'}}, {{'r', 'u', 'n'}}, {{'r', 'u', 'n'}}, {{'r', 'u', 'n'}}}, // Rundi
"ru\0" // Russian LanguageCodeEntry {{{'r', 'u'}}, {{'r', 'u', 's'}}, {{'r', 'u', 's'}}, {{'r', 'u', 's'}}}, // Russian
"rwk" // Rwa LanguageCodeEntry {{}, {}, {}, {{'r', 'w', 'k'}}}, // Rwa
"ssy" // Saho LanguageCodeEntry {{}, {}, {}, {{'s', 's', 'y'}}}, // Saho
"sah" // Sakha LanguageCodeEntry {{}, {{'s', 'a', 'h'}}, {{'s', 'a', 'h'}}, {{'s', 'a', 'h'}}}, // Sakha
"saq" // Samburu LanguageCodeEntry {{}, {}, {}, {{'s', 'a', 'q'}}}, // Samburu
"sm\0" // Samoan LanguageCodeEntry {{{'s', 'm'}}, {{'s', 'm', 'o'}}, {{'s', 'm', 'o'}}, {{'s', 'm', 'o'}}}, // Samoan
"sg\0" // Sango LanguageCodeEntry {{{'s', 'g'}}, {{'s', 'a', 'g'}}, {{'s', 'a', 'g'}}, {{'s', 'a', 'g'}}}, // Sango
"sbp" // Sangu LanguageCodeEntry {{}, {}, {}, {{'s', 'b', 'p'}}}, // Sangu
"sa\0" // Sanskrit LanguageCodeEntry {{{'s', 'a'}}, {{'s', 'a', 'n'}}, {{'s', 'a', 'n'}}, {{'s', 'a', 'n'}}}, // Sanskrit
"sat" // Santali LanguageCodeEntry {{}, {{'s', 'a', 't'}}, {{'s', 'a', 't'}}, {{'s', 'a', 't'}}}, // Santali
"sc\0" // Sardinian LanguageCodeEntry {{{'s', 'c'}}, {{'s', 'r', 'd'}}, {{'s', 'r', 'd'}}, {{'s', 'r', 'd'}}}, // Sardinian
"saz" // Saurashtra LanguageCodeEntry {{}, {}, {}, {{'s', 'a', 'z'}}}, // Saurashtra
"seh" // Sena LanguageCodeEntry {{}, {}, {}, {{'s', 'e', 'h'}}}, // Sena
"sr\0" // Serbian LanguageCodeEntry {{{'s', 'r'}}, {{'s', 'r', 'p'}}, {{'s', 'r', 'p'}}, {{'s', 'r', 'p'}}}, // Serbian
"ksb" // Shambala LanguageCodeEntry {{}, {}, {}, {{'k', 's', 'b'}}}, // Shambala
"sn\0" // Shona LanguageCodeEntry {{{'s', 'n'}}, {{'s', 'n', 'a'}}, {{'s', 'n', 'a'}}, {{'s', 'n', 'a'}}}, // Shona
"ii\0" // Sichuan Yi LanguageCodeEntry {{{'i', 'i'}}, {{'i', 'i', 'i'}}, {{'i', 'i', 'i'}}, {{'i', 'i', 'i'}}}, // Sichuan Yi
"scn" // Sicilian LanguageCodeEntry {{}, {{'s', 'c', 'n'}}, {{'s', 'c', 'n'}}, {{'s', 'c', 'n'}}}, // Sicilian
"sid" // Sidamo LanguageCodeEntry {{}, {{'s', 'i', 'd'}}, {{'s', 'i', 'd'}}, {{'s', 'i', 'd'}}}, // Sidamo
"szl" // Silesian LanguageCodeEntry {{}, {}, {}, {{'s', 'z', 'l'}}}, // Silesian
"sd\0" // Sindhi LanguageCodeEntry {{{'s', 'd'}}, {{'s', 'n', 'd'}}, {{'s', 'n', 'd'}}, {{'s', 'n', 'd'}}}, // Sindhi
"si\0" // Sinhala LanguageCodeEntry {{{'s', 'i'}}, {{'s', 'i', 'n'}}, {{'s', 'i', 'n'}}, {{'s', 'i', 'n'}}}, // Sinhala
"sms" // Skolt Sami LanguageCodeEntry {{}, {{'s', 'm', 's'}}, {{'s', 'm', 's'}}, {{'s', 'm', 's'}}}, // Skolt Sami
"sk\0" // Slovak LanguageCodeEntry {{{'s', 'k'}}, {{'s', 'l', 'o'}}, {{'s', 'l', 'k'}}, {{'s', 'l', 'k'}}}, // Slovak
"sl\0" // Slovenian LanguageCodeEntry {{{'s', 'l'}}, {{'s', 'l', 'v'}}, {{'s', 'l', 'v'}}, {{'s', 'l', 'v'}}}, // Slovenian
"xog" // Soga LanguageCodeEntry {{}, {}, {}, {{'x', 'o', 'g'}}}, // Soga
"so\0" // Somali LanguageCodeEntry {{{'s', 'o'}}, {{'s', 'o', 'm'}}, {{'s', 'o', 'm'}}, {{'s', 'o', 'm'}}}, // Somali
"sdh" // Southern Kurdish LanguageCodeEntry {{}, {}, {}, {{'s', 'd', 'h'}}}, // Southern Kurdish
"sma" // Southern Sami LanguageCodeEntry {{}, {{'s', 'm', 'a'}}, {{'s', 'm', 'a'}}, {{'s', 'm', 'a'}}}, // Southern Sami
"st\0" // Southern Sotho LanguageCodeEntry {{{'s', 't'}}, {{'s', 'o', 't'}}, {{'s', 'o', 't'}}, {{'s', 'o', 't'}}}, // Southern Sotho
"nr\0" // South Ndebele LanguageCodeEntry {{{'n', 'r'}}, {{'n', 'b', 'l'}}, {{'n', 'b', 'l'}}, {{'n', 'b', 'l'}}}, // South Ndebele
"es\0" // Spanish LanguageCodeEntry {{{'e', 's'}}, {{'s', 'p', 'a'}}, {{'s', 'p', 'a'}}, {{'s', 'p', 'a'}}}, // Spanish
"zgh" // Standard Moroccan Tamazight LanguageCodeEntry {{}, {{'z', 'g', 'h'}}, {{'z', 'g', 'h'}}, {{'z', 'g', 'h'}}}, // Standard Moroccan Tamazight
"su\0" // Sundanese LanguageCodeEntry {{{'s', 'u'}}, {{'s', 'u', 'n'}}, {{'s', 'u', 'n'}}, {{'s', 'u', 'n'}}}, // Sundanese
"sw\0" // Swahili LanguageCodeEntry {{{'s', 'w'}}, {{'s', 'w', 'a'}}, {{'s', 'w', 'a'}}, {{'s', 'w', 'a'}}}, // Swahili
"ss\0" // Swati LanguageCodeEntry {{{'s', 's'}}, {{'s', 's', 'w'}}, {{'s', 's', 'w'}}, {{'s', 's', 'w'}}}, // Swati
"sv\0" // Swedish LanguageCodeEntry {{{'s', 'v'}}, {{'s', 'w', 'e'}}, {{'s', 'w', 'e'}}, {{'s', 'w', 'e'}}}, // Swedish
"gsw" // Swiss German LanguageCodeEntry {{}, {{'g', 's', 'w'}}, {{'g', 's', 'w'}}, {{'g', 's', 'w'}}}, // Swiss German
"syr" // Syriac LanguageCodeEntry {{}, {{'s', 'y', 'r'}}, {{'s', 'y', 'r'}}, {{'s', 'y', 'r'}}}, // Syriac
"shi" // Tachelhit LanguageCodeEntry {{}, {}, {}, {{'s', 'h', 'i'}}}, // Tachelhit
"ty\0" // Tahitian LanguageCodeEntry {{{'t', 'y'}}, {{'t', 'a', 'h'}}, {{'t', 'a', 'h'}}, {{'t', 'a', 'h'}}}, // Tahitian
"blt" // Tai Dam LanguageCodeEntry {{}, {}, {}, {{'b', 'l', 't'}}}, // Tai Dam
"dav" // Taita LanguageCodeEntry {{}, {}, {}, {{'d', 'a', 'v'}}}, // Taita
"tg\0" // Tajik LanguageCodeEntry {{{'t', 'g'}}, {{'t', 'g', 'k'}}, {{'t', 'g', 'k'}}, {{'t', 'g', 'k'}}}, // Tajik
"ta\0" // Tamil LanguageCodeEntry {{{'t', 'a'}}, {{'t', 'a', 'm'}}, {{'t', 'a', 'm'}}, {{'t', 'a', 'm'}}}, // Tamil
"trv" // Taroko LanguageCodeEntry {{}, {}, {}, {{'t', 'r', 'v'}}}, // Taroko
"twq" // Tasawaq LanguageCodeEntry {{}, {}, {}, {{'t', 'w', 'q'}}}, // Tasawaq
"tt\0" // Tatar LanguageCodeEntry {{{'t', 't'}}, {{'t', 'a', 't'}}, {{'t', 'a', 't'}}, {{'t', 'a', 't'}}}, // Tatar
"te\0" // Telugu LanguageCodeEntry {{{'t', 'e'}}, {{'t', 'e', 'l'}}, {{'t', 'e', 'l'}}, {{'t', 'e', 'l'}}}, // Telugu
"teo" // Teso LanguageCodeEntry {{}, {}, {}, {{'t', 'e', 'o'}}}, // Teso
"th\0" // Thai LanguageCodeEntry {{{'t', 'h'}}, {{'t', 'h', 'a'}}, {{'t', 'h', 'a'}}, {{'t', 'h', 'a'}}}, // Thai
"bo\0" // Tibetan LanguageCodeEntry {{{'b', 'o'}}, {{'t', 'i', 'b'}}, {{'b', 'o', 'd'}}, {{'b', 'o', 'd'}}}, // Tibetan
"tig" // Tigre LanguageCodeEntry {{}, {{'t', 'i', 'g'}}, {{'t', 'i', 'g'}}, {{'t', 'i', 'g'}}}, // Tigre
"ti\0" // Tigrinya LanguageCodeEntry {{{'t', 'i'}}, {{'t', 'i', 'r'}}, {{'t', 'i', 'r'}}, {{'t', 'i', 'r'}}}, // Tigrinya
"tkl" // Tokelau LanguageCodeEntry {{}, {{'t', 'k', 'l'}}, {{'t', 'k', 'l'}}, {{'t', 'k', 'l'}}}, // Tokelau
"tpi" // Tok Pisin LanguageCodeEntry {{}, {{'t', 'p', 'i'}}, {{'t', 'p', 'i'}}, {{'t', 'p', 'i'}}}, // Tok Pisin
"to\0" // Tongan LanguageCodeEntry {{{'t', 'o'}}, {{'t', 'o', 'n'}}, {{'t', 'o', 'n'}}, {{'t', 'o', 'n'}}}, // Tongan
"ts\0" // Tsonga LanguageCodeEntry {{{'t', 's'}}, {{'t', 's', 'o'}}, {{'t', 's', 'o'}}, {{'t', 's', 'o'}}}, // Tsonga
"tn\0" // Tswana LanguageCodeEntry {{{'t', 'n'}}, {{'t', 's', 'n'}}, {{'t', 's', 'n'}}, {{'t', 's', 'n'}}}, // Tswana
"tr\0" // Turkish LanguageCodeEntry {{{'t', 'r'}}, {{'t', 'u', 'r'}}, {{'t', 'u', 'r'}}, {{'t', 'u', 'r'}}}, // Turkish
"tk\0" // Turkmen LanguageCodeEntry {{{'t', 'k'}}, {{'t', 'u', 'k'}}, {{'t', 'u', 'k'}}, {{'t', 'u', 'k'}}}, // Turkmen
"tvl" // Tuvalu LanguageCodeEntry {{}, {{'t', 'v', 'l'}}, {{'t', 'v', 'l'}}, {{'t', 'v', 'l'}}}, // Tuvalu
"kcg" // Tyap LanguageCodeEntry {{}, {}, {}, {{'k', 'c', 'g'}}}, // Tyap
"uga" // Ugaritic LanguageCodeEntry {{}, {{'u', 'g', 'a'}}, {{'u', 'g', 'a'}}, {{'u', 'g', 'a'}}}, // Ugaritic
"uk\0" // Ukrainian LanguageCodeEntry {{{'u', 'k'}}, {{'u', 'k', 'r'}}, {{'u', 'k', 'r'}}, {{'u', 'k', 'r'}}}, // Ukrainian
"hsb" // Upper Sorbian LanguageCodeEntry {{}, {{'h', 's', 'b'}}, {{'h', 's', 'b'}}, {{'h', 's', 'b'}}}, // Upper Sorbian
"ur\0" // Urdu LanguageCodeEntry {{{'u', 'r'}}, {{'u', 'r', 'd'}}, {{'u', 'r', 'd'}}, {{'u', 'r', 'd'}}}, // Urdu
"ug\0" // Uyghur LanguageCodeEntry {{{'u', 'g'}}, {{'u', 'i', 'g'}}, {{'u', 'i', 'g'}}, {{'u', 'i', 'g'}}}, // Uyghur
"uz\0" // Uzbek LanguageCodeEntry {{{'u', 'z'}}, {{'u', 'z', 'b'}}, {{'u', 'z', 'b'}}, {{'u', 'z', 'b'}}}, // Uzbek
"vai" // Vai LanguageCodeEntry {{}, {{'v', 'a', 'i'}}, {{'v', 'a', 'i'}}, {{'v', 'a', 'i'}}}, // Vai
"ve\0" // Venda LanguageCodeEntry {{{'v', 'e'}}, {{'v', 'e', 'n'}}, {{'v', 'e', 'n'}}, {{'v', 'e', 'n'}}}, // Venda
"vi\0" // Vietnamese LanguageCodeEntry {{{'v', 'i'}}, {{'v', 'i', 'e'}}, {{'v', 'i', 'e'}}, {{'v', 'i', 'e'}}}, // Vietnamese
"vo\0" // Volapuk LanguageCodeEntry {{{'v', 'o'}}, {{'v', 'o', 'l'}}, {{'v', 'o', 'l'}}, {{'v', 'o', 'l'}}}, // Volapuk
"vun" // Vunjo LanguageCodeEntry {{}, {}, {}, {{'v', 'u', 'n'}}}, // Vunjo
"wa\0" // Walloon LanguageCodeEntry {{{'w', 'a'}}, {{'w', 'l', 'n'}}, {{'w', 'l', 'n'}}, {{'w', 'l', 'n'}}}, // Walloon
"wae" // Walser LanguageCodeEntry {{}, {}, {}, {{'w', 'a', 'e'}}}, // Walser
"wbp" // Warlpiri LanguageCodeEntry {{}, {}, {}, {{'w', 'b', 'p'}}}, // Warlpiri
"cy\0" // Welsh LanguageCodeEntry {{{'c', 'y'}}, {{'w', 'e', 'l'}}, {{'c', 'y', 'm'}}, {{'c', 'y', 'm'}}}, // Welsh
"bgn" // Western Balochi LanguageCodeEntry {{}, {}, {}, {{'b', 'g', 'n'}}}, // Western Balochi
"fy\0" // Western Frisian LanguageCodeEntry {{{'f', 'y'}}, {{'f', 'r', 'y'}}, {{'f', 'r', 'y'}}, {{'f', 'r', 'y'}}}, // Western Frisian
"wal" // Wolaytta LanguageCodeEntry {{}, {{'w', 'a', 'l'}}, {{'w', 'a', 'l'}}, {{'w', 'a', 'l'}}}, // Wolaytta
"wo\0" // Wolof LanguageCodeEntry {{{'w', 'o'}}, {{'w', 'o', 'l'}}, {{'w', 'o', 'l'}}, {{'w', 'o', 'l'}}}, // Wolof
"xh\0" // Xhosa LanguageCodeEntry {{{'x', 'h'}}, {{'x', 'h', 'o'}}, {{'x', 'h', 'o'}}, {{'x', 'h', 'o'}}}, // Xhosa
"yav" // Yangben LanguageCodeEntry {{}, {}, {}, {{'y', 'a', 'v'}}}, // Yangben
"yi\0" // Yiddish LanguageCodeEntry {{{'y', 'i'}}, {{'y', 'i', 'd'}}, {{'y', 'i', 'd'}}, {{'y', 'i', 'd'}}}, // Yiddish
"yo\0" // Yoruba LanguageCodeEntry {{{'y', 'o'}}, {{'y', 'o', 'r'}}, {{'y', 'o', 'r'}}, {{'y', 'o', 'r'}}}, // Yoruba
"dje" // Zarma LanguageCodeEntry {{}, {}, {}, {{'d', 'j', 'e'}}}, // Zarma
"za\0" // Zhuang LanguageCodeEntry {{{'z', 'a'}}, {{'z', 'h', 'a'}}, {{'z', 'h', 'a'}}, {{'z', 'h', 'a'}}}, // Zhuang
"zu\0" // Zulu LanguageCodeEntry {{{'z', 'u'}}, {{'z', 'u', 'l'}}, {{'z', 'u', 'l'}}, {{'z', 'u', 'l'}}}, // Zulu
"kgp" // Kaingang LanguageCodeEntry {{}, {}, {}, {{'k', 'g', 'p'}}}, // Kaingang
"yrl" // Nheengatu LanguageCodeEntry {{}, {}, {}, {{'y', 'r', 'l'}}}, // Nheengatu
; };
static const unsigned char script_code_list[] = static const unsigned char script_code_list[] =
"Zzzz" // AnyScript "Zzzz" // AnyScript

View File

@ -548,12 +548,18 @@ static QVariant getLocaleValue(CFStringRef key)
return QVariant(); return QVariant();
} }
static QLocale::Language codeToLanguage(QStringView s)
{
return QLocalePrivate::codeToLanguage(s);
}
QVariant QSystemLocale::query(QueryType type, QVariant in) const QVariant QSystemLocale::query(QueryType type, QVariant in) const
{ {
QMacAutoReleasePool pool; QMacAutoReleasePool pool;
switch(type) { switch(type) {
case LanguageId: case LanguageId:
return getLocaleValue<QLocalePrivate::codeToLanguage>(kCFLocaleLanguageCode); return getLocaleValue<codeToLanguage>(kCFLocaleLanguageCode);
case TerritoryId: case TerritoryId:
return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode); return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode);
case ScriptId: case ScriptId:

View File

@ -418,18 +418,25 @@ public:
[[nodiscard]] QByteArray bcp47Name(char separator = '-') const; [[nodiscard]] QByteArray bcp47Name(char separator = '-') const;
[[nodiscard]] inline QLatin1String languageCode() const [[nodiscard]] inline QLatin1String
{ return languageToCode(QLocale::Language(m_data->m_language_id)); } languageCode(QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) const
{
return languageToCode(QLocale::Language(m_data->m_language_id), codeTypes);
}
[[nodiscard]] inline QLatin1String scriptCode() const [[nodiscard]] inline QLatin1String scriptCode() const
{ return scriptToCode(QLocale::Script(m_data->m_script_id)); } { return scriptToCode(QLocale::Script(m_data->m_script_id)); }
[[nodiscard]] inline QLatin1String territoryCode() const [[nodiscard]] inline QLatin1String territoryCode() const
{ return territoryToCode(QLocale::Territory(m_data->m_territory_id)); } { return territoryToCode(QLocale::Territory(m_data->m_territory_id)); }
[[nodiscard]] static const QLocalePrivate *get(const QLocale &l) { return l.d; } [[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 scriptToCode(QLocale::Script script);
[[nodiscard]] static QLatin1String territoryToCode(QLocale::Territory territory); [[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::Script codeToScript(QStringView code) noexcept;
[[nodiscard]] static QLocale::Territory codeToTerritory(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::AnyLanguage), QString());
QCOMPARE(QLocale::languageToCode(QLocale::C), QString("C")); QCOMPARE(QLocale::languageToCode(QLocale::C), QString("C"));
QCOMPARE(QLocale::languageToCode(QLocale::English), QString("en")); 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::AnyTerritory), QString());
QCOMPARE(QLocale::territoryToCode(QLocale::UnitedStates), QString("US")); 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("e")), QLocale::AnyLanguage);
QCOMPARE(QLocale::codeToLanguage(QString("en")), QLocale::English); QCOMPARE(QLocale::codeToLanguage(QString("en")), QLocale::English);
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::Hausa);
QCOMPARE(QLocale::codeToLanguage(QString("ha"), QLocale::ISO639Alpha3), QLocale::AnyLanguage);
QCOMPARE(QLocale::codeToLanguage(QString("haw")), QLocale::Hawaiian); 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()), QLocale::AnyTerritory);
QCOMPARE(QLocale::codeToTerritory(QString("ZZ")), 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. See ``cldr2qlocalexml.py`` for how to generate the QLocaleXML data itself.
Pass the output file from that as first parameter to this script; pass 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 datetime
import argparse import argparse
from pathlib import Path from pathlib import Path
from typing import Optional
from qlocalexml import QLocaleXmlReader from qlocalexml import QLocaleXmlReader
from localetools import unicode2hex, wrap_list, Error, Transcriber, SourceFileEditor from localetools import unicode2hex, wrap_list, Error, Transcriber, SourceFileEditor
from iso639_3 import LanguageCodeData
class LocaleKeySorter: class LocaleKeySorter:
"""Sort-ordering representation of a locale key. """Sort-ordering representation of a locale key.
@ -389,8 +396,42 @@ class LocaleDataWriter (LocaleSourceEditor):
# TODO: unify these next three into the previous three; kept # TODO: unify these next three into the previous three; kept
# separate for now to verify we're not changing data. # separate for now to verify we're not changing data.
def languageCodes(self, languages): def languageCodes(self, languages, code_data: LanguageCodeData):
self.__writeCodeList(self.writer.write, languages, 'language', 3) 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): def scriptCodes(self, scripts):
self.__writeCodeList(self.writer.write, scripts, 'script', 4) self.__writeCodeList(self.writer.write, scripts, 'script', 4)
@ -519,6 +560,8 @@ def main(out, err):
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('input_file', help='input XML file name', parser.add_argument('input_file', help='input XML file name',
metavar='input-file.xml') 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('qtbase_path', help='path to the root of the qtbase source tree')
parser.add_argument('--calendars', help='select calendars to emit data for', parser.add_argument('--calendars', help='select calendars to emit data for',
nargs='+', metavar='CALENDAR', nargs='+', metavar='CALENDAR',
@ -538,6 +581,8 @@ def main(out, err):
locale_map = dict(reader.loadLocaleMap(calendars, err.write)) locale_map = dict(reader.loadLocaleMap(calendars, err.write))
locale_keys = sorted(locale_map.keys(), key=LocaleKeySorter(reader.defaultMap())) locale_keys = sorted(locale_map.keys(), key=LocaleKeySorter(reader.defaultMap()))
code_data = LanguageCodeData(args.iso_path)
try: try:
with LocaleDataWriter(qtsrcdir.joinpath('src/corelib/text/qlocale_data_p.h'), with LocaleDataWriter(qtsrcdir.joinpath('src/corelib/text/qlocale_data_p.h'),
qtsrcdir, reader.cldrVersion) as writer: qtsrcdir, reader.cldrVersion) as writer:
@ -549,7 +594,7 @@ def main(out, err):
writer.scriptNames(reader.scripts) writer.scriptNames(reader.scripts)
writer.territoryNames(reader.territories) writer.territoryNames(reader.territories)
# TODO: merge the next three into the previous three # TODO: merge the next three into the previous three
writer.languageCodes(reader.languages) writer.languageCodes(reader.languages, code_data)
writer.scriptCodes(reader.scripts) writer.scriptCodes(reader.scripts)
writer.territoryCodes(reader.territories) writer.territoryCodes(reader.territories)
except Exception as e: except Exception as e: