summaryrefslogtreecommitdiffstats
path: root/intl/locale/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--intl/locale/tests/gtest/TestCollation.cpp157
-rw-r--r--intl/locale/tests/gtest/TestDateTimeFormat.cpp240
-rw-r--r--intl/locale/tests/gtest/TestLocaleService.cpp154
-rw-r--r--intl/locale/tests/gtest/TestLocaleServiceNegotiate.cpp53
-rw-r--r--intl/locale/tests/gtest/TestMozLocale.cpp123
-rw-r--r--intl/locale/tests/gtest/TestOSPreferences.cpp203
-rw-r--r--intl/locale/tests/gtest/moz.build16
-rw-r--r--intl/locale/tests/sort/us-ascii_base.txt95
-rw-r--r--intl/locale/tests/sort/us-ascii_base_case_res.txt96
-rw-r--r--intl/locale/tests/sort/us-ascii_base_nocase_res.txt96
-rw-r--r--intl/locale/tests/sort/us-ascii_sort.txt78
-rw-r--r--intl/locale/tests/sort/us-ascii_sort_case_res.txt79
-rw-r--r--intl/locale/tests/sort/us-ascii_sort_nocase_res.txt79
-rw-r--r--intl/locale/tests/unit/data/chrome.manifest1
-rw-r--r--intl/locale/tests/unit/data/intl_on_workers_worker.js4
-rw-r--r--intl/locale/tests/unit/test_bug1086527.js22
-rw-r--r--intl/locale/tests/unit/test_bug22310.js65
-rw-r--r--intl/locale/tests/unit/test_intl_on_workers.js29
-rw-r--r--intl/locale/tests/unit/test_localeService.js241
-rw-r--r--intl/locale/tests/unit/test_localeService_negotiateLanguages.js205
-rw-r--r--intl/locale/tests/unit/test_osPreferences.js44
-rw-r--r--intl/locale/tests/unit/test_pluralForm.js6150
-rw-r--r--intl/locale/tests/unit/test_pluralForm_english.js31
-rw-r--r--intl/locale/tests/unit/test_pluralForm_makeGetter.js38
-rw-r--r--intl/locale/tests/unit/xpcshell.ini21
25 files changed, 8320 insertions, 0 deletions
diff --git a/intl/locale/tests/gtest/TestCollation.cpp b/intl/locale/tests/gtest/TestCollation.cpp
new file mode 100644
index 0000000000..4085f6433f
--- /dev/null
+++ b/intl/locale/tests/gtest/TestCollation.cpp
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsCollationCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsICollation.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+TEST(Collation, AllocateRowSortKey)
+{
+ nsCOMPtr<nsICollationFactory> colFactory =
+ do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
+ ASSERT_TRUE(colFactory);
+
+ // Don't throw error even if locale name is invalid
+ nsCOMPtr<nsICollation> collator;
+ nsresult rv = colFactory->CreateCollationForLocale("$languageName"_ns,
+ getter_AddRefs(collator));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<uint8_t> sortKey1;
+ // Don't throw error even if locale name is invalid
+ rv = collator->AllocateRawSortKey(nsICollation::kCollationStrengthDefault,
+ u"ABC"_ns, sortKey1);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsTArray<uint8_t> sortKey2;
+ // Don't throw error even if locale name is invalid
+ rv = collator->AllocateRawSortKey(nsICollation::kCollationStrengthDefault,
+ u"DEF"_ns, sortKey2);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ int32_t result;
+ rv = collator->CompareRawSortKey(sortKey1, sortKey2, &result);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ ASSERT_TRUE(result < 0);
+}
+
+class CollationComparator final {
+ public:
+ explicit CollationComparator(nsICollation* aCollation)
+ : mCollation(aCollation) {}
+
+ bool Equals(const nsString& a, const nsString& b) const {
+ int32_t result = 0;
+ mCollation->CompareString(nsICollation::kCollationStrengthDefault, a, b,
+ &result);
+ return result == 0;
+ }
+
+ bool LessThan(const nsString& a, const nsString& b) const {
+ int32_t result = 0;
+ mCollation->CompareString(nsICollation::kCollationStrengthDefault, a, b,
+ &result);
+ return result < 0;
+ }
+
+ private:
+ nsCOMPtr<nsICollation> mCollation;
+};
+
+TEST(Collation, CompareString)
+{
+ nsTArray<nsString> input;
+ input.AppendElement(u"Argentina"_ns);
+ input.AppendElement(u"Oerlikon"_ns);
+ input.AppendElement(u"Offenbach"_ns);
+ input.AppendElement(u"Sverige"_ns);
+ input.AppendElement(u"Vaticano"_ns);
+ input.AppendElement(u"Zimbabwe"_ns);
+ input.AppendElement(u"la France"_ns);
+ input.AppendElement(u"\u00a1viva Espa\u00f1a!"_ns);
+ input.AppendElement(u"\u00d6sterreich"_ns);
+ input.AppendElement(u"\u4e2d\u56fd"_ns);
+ input.AppendElement(u"\u65e5\u672c"_ns);
+ input.AppendElement(u"\ud55c\uad6d"_ns);
+
+ nsCOMPtr<nsICollationFactory> colFactory =
+ do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
+ ASSERT_TRUE(colFactory);
+
+ // Locale en-US; default options.
+ nsCOMPtr<nsICollation> collation;
+ colFactory->CreateCollationForLocale("en-US"_ns, getter_AddRefs(collation));
+ ASSERT_TRUE(collation);
+
+ {
+ CollationComparator comparator(collation);
+ input.Sort(comparator);
+
+ ASSERT_TRUE(input[0].Equals(u"\u00a1viva Espa\u00f1a!"_ns));
+ ASSERT_TRUE(input[1].Equals(u"Argentina"_ns));
+ ASSERT_TRUE(input[2].Equals(u"la France"_ns));
+ ASSERT_TRUE(input[3].Equals(u"Oerlikon"_ns));
+ ASSERT_TRUE(input[4].Equals(u"Offenbach"_ns));
+ ASSERT_TRUE(input[5].Equals(u"\u00d6sterreich"_ns));
+ ASSERT_TRUE(input[6].Equals(u"Sverige"_ns));
+ ASSERT_TRUE(input[7].Equals(u"Vaticano"_ns));
+ ASSERT_TRUE(input[8].Equals(u"Zimbabwe"_ns));
+ ASSERT_TRUE(input[9].Equals(u"\ud55c\uad6d"_ns));
+ ASSERT_TRUE(input[10].Equals(u"\u4e2d\u56fd"_ns));
+ ASSERT_TRUE(input[11].Equals(u"\u65e5\u672c"_ns));
+ }
+
+ // Locale sv-SE; default options.
+ // Swedish treats "Ö" as a separate character, which sorts after "Z".
+ colFactory->CreateCollationForLocale("sv-SE"_ns, getter_AddRefs(collation));
+ ASSERT_TRUE(collation);
+
+ {
+ CollationComparator comparator(collation);
+ input.Sort(comparator);
+
+ ASSERT_TRUE(input[0].Equals(u"\u00a1viva Espa\u00f1a!"_ns));
+ ASSERT_TRUE(input[1].Equals(u"Argentina"_ns));
+ ASSERT_TRUE(input[2].Equals(u"la France"_ns));
+ ASSERT_TRUE(input[3].Equals(u"Oerlikon"_ns));
+ ASSERT_TRUE(input[4].Equals(u"Offenbach"_ns));
+ ASSERT_TRUE(input[5].Equals(u"Sverige"_ns));
+ ASSERT_TRUE(input[6].Equals(u"Vaticano"_ns));
+ ASSERT_TRUE(input[7].Equals(u"Zimbabwe"_ns));
+ ASSERT_TRUE(input[8].Equals(u"\u00d6sterreich"_ns));
+ ASSERT_TRUE(input[9].Equals(u"\ud55c\uad6d"_ns));
+ ASSERT_TRUE(input[10].Equals(u"\u4e2d\u56fd"_ns));
+ ASSERT_TRUE(input[11].Equals(u"\u65e5\u672c"_ns));
+ }
+
+ // Locale de-DE; default options.
+ // In German standard sorting, umlauted characters are treated as variants
+ // of their base characters: ä ≅ a, ö ≅ o, ü ≅ u.
+ colFactory->CreateCollationForLocale("de-DE"_ns, getter_AddRefs(collation));
+ ASSERT_TRUE(collation);
+
+ {
+ CollationComparator comparator(collation);
+ input.Sort(comparator);
+
+ ASSERT_TRUE(input[0].Equals(u"\u00a1viva Espa\u00f1a!"_ns));
+ ASSERT_TRUE(input[1].Equals(u"Argentina"_ns));
+ ASSERT_TRUE(input[2].Equals(u"la France"_ns));
+ ASSERT_TRUE(input[3].Equals(u"Oerlikon"_ns));
+ ASSERT_TRUE(input[4].Equals(u"Offenbach"_ns));
+ ASSERT_TRUE(input[5].Equals(u"\u00d6sterreich"_ns));
+ ASSERT_TRUE(input[6].Equals(u"Sverige"_ns));
+ ASSERT_TRUE(input[7].Equals(u"Vaticano"_ns));
+ ASSERT_TRUE(input[8].Equals(u"Zimbabwe"_ns));
+ ASSERT_TRUE(input[9].Equals(u"\ud55c\uad6d"_ns));
+ ASSERT_TRUE(input[10].Equals(u"\u4e2d\u56fd"_ns));
+ ASSERT_TRUE(input[11].Equals(u"\u65e5\u672c"_ns));
+ }
+}
diff --git a/intl/locale/tests/gtest/TestDateTimeFormat.cpp b/intl/locale/tests/gtest/TestDateTimeFormat.cpp
new file mode 100644
index 0000000000..e2e56014cf
--- /dev/null
+++ b/intl/locale/tests/gtest/TestDateTimeFormat.cpp
@@ -0,0 +1,240 @@
+#include "gtest/gtest.h"
+#include "DateTimeFormat.h"
+
+namespace mozilla {
+
+TEST(DateTimeFormat, FormatPRExplodedTime)
+{
+ PRTime prTime = 0;
+ PRExplodedTime prExplodedTime;
+ PR_ExplodeTime(prTime, PR_GMTParameters, &prExplodedTime);
+
+ mozilla::DateTimeFormat::mLocale = new nsCString("en-US");
+ mozilla::DateTimeFormat::DeleteCache();
+
+ nsAutoString formattedTime;
+ nsresult rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("January") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("12:00:00 AM") != kNotFound ||
+ formattedTime.Find("00:00:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 19, 0, 1, 0, 1970, 4, 0, {(19 * 60), 0}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("January") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("12:19:00 AM") != kNotFound ||
+ formattedTime.Find("00:19:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 0, 7, 1,
+ 0, 1970, 4, 0, {(6 * 60 * 60), (1 * 60 * 60)}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("January") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("7:00:00 AM") != kNotFound ||
+ formattedTime.Find("07:00:00") != kNotFound);
+
+ prExplodedTime = {
+ 0, 0, 29, 11, 1,
+ 0, 1970, 4, 0, {(10 * 60 * 60) + (29 * 60), (1 * 60 * 60)}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("January") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("11:29:00 AM") != kNotFound ||
+ formattedTime.Find("11:29:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 37, 23, 31, 11, 1969, 3, 364, {-(23 * 60), 0}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("December") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("31") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1969") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("11:37:00 PM") != kNotFound ||
+ formattedTime.Find("23:37:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 0, 17, 31, 11, 1969, 3, 364, {-(7 * 60 * 60), 0}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("December") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("31") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1969") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("5:00:00 PM") != kNotFound ||
+ formattedTime.Find("17:00:00") != kNotFound);
+
+ prExplodedTime = {
+ 0, 0, 47, 14, 31,
+ 11, 1969, 3, 364, {-((10 * 60 * 60) + (13 * 60)), (1 * 60 * 60)}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("December") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("31") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1969") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("2:47:00 PM") != kNotFound ||
+ formattedTime.Find("14:47:00") != kNotFound);
+}
+
+TEST(DateTimeFormat, DateFormatSelectors)
+{
+ PRTime prTime = 0;
+ PRExplodedTime prExplodedTime;
+ PR_ExplodeTime(prTime, PR_GMTParameters, &prExplodedTime);
+
+ mozilla::DateTimeFormat::mLocale = new nsCString("en-US");
+ mozilla::DateTimeFormat::DeleteCache();
+
+ nsAutoString formattedTime;
+ nsresult rv = mozilla::DateTimeFormat::FormatDateTime(
+ &prExplodedTime, DateTimeFormat::Skeleton::yyyyMM, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("01/1970", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+ rv = mozilla::DateTimeFormat::FormatDateTime(
+ &prExplodedTime, DateTimeFormat::Skeleton::yyyyMMMM, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("January 1970", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+ rv = mozilla::DateTimeFormat::GetCalendarSymbol(
+ mozilla::DateTimeFormat::Field::Month,
+ mozilla::DateTimeFormat::Style::Wide, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("January", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+ rv = mozilla::DateTimeFormat::GetCalendarSymbol(
+ mozilla::DateTimeFormat::Field::Weekday,
+ mozilla::DateTimeFormat::Style::Abbreviated, &prExplodedTime,
+ formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("Thu", NS_ConvertUTF16toUTF8(formattedTime).get());
+}
+
+TEST(DateTimeFormat, FormatPRExplodedTimeForeign)
+{
+ PRTime prTime = 0;
+ PRExplodedTime prExplodedTime;
+ PR_ExplodeTime(prTime, PR_GMTParameters, &prExplodedTime);
+
+ mozilla::DateTimeFormat::mLocale = new nsCString("de-DE");
+ mozilla::DateTimeFormat::DeleteCache();
+
+ nsAutoString formattedTime;
+ nsresult rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("1.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Januar") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("12:00:00 AM") != kNotFound ||
+ formattedTime.Find("00:00:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 19, 0, 1, 0, 1970, 4, 0, {(19 * 60), 0}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("1.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Januar") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("12:19:00 AM") != kNotFound ||
+ formattedTime.Find("00:19:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 0, 7, 1,
+ 0, 1970, 4, 0, {(6 * 60 * 60), (1 * 60 * 60)}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("1.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Januar") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("7:00:00 AM") != kNotFound ||
+ formattedTime.Find("07:00:00") != kNotFound);
+
+ prExplodedTime = {
+ 0, 0, 29, 11, 1,
+ 0, 1970, 4, 0, {(10 * 60 * 60) + (29 * 60), (1 * 60 * 60)}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("1.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Januar") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1970") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("11:29:00 AM") != kNotFound ||
+ formattedTime.Find("11:29:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 37, 23, 31, 11, 1969, 3, 364, {-(23 * 60), 0}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("31.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Dezember") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1969") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("11:37:00 PM") != kNotFound ||
+ formattedTime.Find("23:37:00") != kNotFound);
+
+ prExplodedTime = {0, 0, 0, 17, 31, 11, 1969, 3, 364, {-(7 * 60 * 60), 0}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("31.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Dezember") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1969") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("5:00:00 PM") != kNotFound ||
+ formattedTime.Find("17:00:00") != kNotFound);
+
+ prExplodedTime = {
+ 0, 0, 47, 14, 31,
+ 11, 1969, 3, 364, {-((10 * 60 * 60) + (13 * 60)), (1 * 60 * 60)}};
+ rv = mozilla::DateTimeFormat::FormatPRExplodedTime(
+ kDateFormatLong, kTimeFormatLong, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(formattedTime.Find("31.") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("Dezember") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("1969") != kNotFound);
+ ASSERT_TRUE(formattedTime.Find("2:47:00 PM") != kNotFound ||
+ formattedTime.Find("14:47:00") != kNotFound);
+}
+
+TEST(DateTimeFormat, DateFormatSelectorsForeign)
+{
+ PRTime prTime = 0;
+ PRExplodedTime prExplodedTime;
+ PR_ExplodeTime(prTime, PR_GMTParameters, &prExplodedTime);
+
+ mozilla::DateTimeFormat::mLocale = new nsCString("de-DE");
+ mozilla::DateTimeFormat::DeleteCache();
+
+ nsAutoString formattedTime;
+ nsresult rv = mozilla::DateTimeFormat::FormatDateTime(
+ &prExplodedTime, DateTimeFormat::Skeleton::yyyyMM, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("01.1970", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+ rv = mozilla::DateTimeFormat::FormatDateTime(
+ &prExplodedTime, DateTimeFormat::Skeleton::yyyyMMMM, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("Januar 1970", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+ rv = mozilla::DateTimeFormat::GetCalendarSymbol(
+ mozilla::DateTimeFormat::Field::Month,
+ mozilla::DateTimeFormat::Style::Wide, &prExplodedTime, formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("Januar", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+ rv = mozilla::DateTimeFormat::GetCalendarSymbol(
+ mozilla::DateTimeFormat::Field::Weekday,
+ mozilla::DateTimeFormat::Style::Abbreviated, &prExplodedTime,
+ formattedTime);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_STREQ("Do", NS_ConvertUTF16toUTF8(formattedTime).get());
+}
+
+} // namespace mozilla
diff --git a/intl/locale/tests/gtest/TestLocaleService.cpp b/intl/locale/tests/gtest/TestLocaleService.cpp
new file mode 100644
index 0000000000..32bdff61d6
--- /dev/null
+++ b/intl/locale/tests/gtest/TestLocaleService.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/intl/MozLocale.h"
+
+using namespace mozilla::intl;
+
+TEST(Intl_Locale_LocaleService, CanonicalizeLanguageId)
+{
+ nsCString locale("en-US.POSIX");
+ ASSERT_TRUE(LocaleService::CanonicalizeLanguageId(locale));
+ ASSERT_TRUE(locale.EqualsLiteral("en-US"));
+
+ locale.AssignLiteral("en-US_POSIX");
+ ASSERT_TRUE(LocaleService::CanonicalizeLanguageId(locale));
+ ASSERT_TRUE(locale.EqualsLiteral("en-US-posix"));
+
+ locale.AssignLiteral("en-US-POSIX");
+ ASSERT_TRUE(LocaleService::CanonicalizeLanguageId(locale));
+ ASSERT_TRUE(locale.EqualsLiteral("en-US-posix"));
+
+ locale.AssignLiteral("C");
+ ASSERT_FALSE(LocaleService::CanonicalizeLanguageId(locale));
+ ASSERT_TRUE(locale.EqualsLiteral("und"));
+
+ locale.AssignLiteral("");
+ ASSERT_FALSE(LocaleService::CanonicalizeLanguageId(locale));
+ ASSERT_TRUE(locale.EqualsLiteral("und"));
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocalesAsBCP47)
+{
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
+
+ ASSERT_FALSE(appLocales.IsEmpty());
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocalesAsLangTags)
+{
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsLangTags(appLocales);
+
+ ASSERT_FALSE(appLocales.IsEmpty());
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocalesAsLangTags_lastIsPresent)
+{
+ nsAutoCString lastFallbackLocale;
+ LocaleService::GetInstance()->GetLastFallbackLocale(lastFallbackLocale);
+
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsLangTags(appLocales);
+
+ ASSERT_TRUE(appLocales.Contains(lastFallbackLocale));
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocaleAsLangTag)
+{
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsLangTags(appLocales);
+
+ nsAutoCString locale;
+ LocaleService::GetInstance()->GetAppLocaleAsLangTag(locale);
+
+ ASSERT_TRUE(appLocales[0] == locale);
+}
+
+TEST(Intl_Locale_LocaleService, GetRegionalPrefsLocales)
+{
+ nsTArray<nsCString> rpLocales;
+ LocaleService::GetInstance()->GetRegionalPrefsLocales(rpLocales);
+
+ int32_t len = rpLocales.Length();
+ ASSERT_TRUE(len > 0);
+}
+
+TEST(Intl_Locale_LocaleService, GetWebExposedLocales)
+{
+ const nsTArray<nsCString> spoofLocale{"de"_ns};
+ LocaleService::GetInstance()->SetAvailableLocales(spoofLocale);
+ LocaleService::GetInstance()->SetRequestedLocales(spoofLocale);
+
+ nsTArray<nsCString> pvLocales;
+
+ mozilla::Preferences::SetInt("privacy.spoof_english", 0);
+ LocaleService::GetInstance()->GetWebExposedLocales(pvLocales);
+ ASSERT_TRUE(pvLocales.Length() > 0);
+ ASSERT_TRUE(pvLocales[0].Equals("de"_ns));
+
+ mozilla::Preferences::SetCString("intl.locale.privacy.web_exposed", "zh-TW");
+ LocaleService::GetInstance()->GetWebExposedLocales(pvLocales);
+ ASSERT_TRUE(pvLocales.Length() > 0);
+ ASSERT_TRUE(pvLocales[0].Equals("zh-TW"_ns));
+
+ mozilla::Preferences::SetInt("privacy.spoof_english", 2);
+ LocaleService::GetInstance()->GetWebExposedLocales(pvLocales);
+ ASSERT_EQ(1u, pvLocales.Length());
+ ASSERT_TRUE(pvLocales[0].Equals("en-US"_ns));
+}
+
+TEST(Intl_Locale_LocaleService, GetRequestedLocales)
+{
+ nsTArray<nsCString> reqLocales;
+ LocaleService::GetInstance()->GetRequestedLocales(reqLocales);
+
+ int32_t len = reqLocales.Length();
+ ASSERT_TRUE(len > 0);
+}
+
+TEST(Intl_Locale_LocaleService, GetAvailableLocales)
+{
+ nsTArray<nsCString> availableLocales;
+ LocaleService::GetInstance()->GetAvailableLocales(availableLocales);
+
+ int32_t len = availableLocales.Length();
+ ASSERT_TRUE(len > 0);
+}
+
+TEST(Intl_Locale_LocaleService, GetPackagedLocales)
+{
+ nsTArray<nsCString> packagedLocales;
+ LocaleService::GetInstance()->GetPackagedLocales(packagedLocales);
+
+ int32_t len = packagedLocales.Length();
+ ASSERT_TRUE(len > 0);
+}
+
+TEST(Intl_Locale_LocaleService, GetDefaultLocale)
+{
+ nsAutoCString locStr;
+ LocaleService::GetInstance()->GetDefaultLocale(locStr);
+
+ ASSERT_FALSE(locStr.IsEmpty());
+ ASSERT_TRUE(Locale(locStr).IsWellFormed());
+}
+
+TEST(Intl_Locale_LocaleService, IsAppLocaleRTL)
+{
+ mozilla::Preferences::SetCString("intl.l10n.pseudo", "bidi");
+ ASSERT_TRUE(LocaleService::GetInstance()->IsAppLocaleRTL());
+ mozilla::Preferences::ClearUser("intl.l10n.pseudo");
+
+ mozilla::Preferences::SetInt("intl.uidirection", 0);
+ ASSERT_FALSE(LocaleService::GetInstance()->IsAppLocaleRTL());
+ mozilla::Preferences::SetInt("intl.uidirection", 1);
+ ASSERT_TRUE(LocaleService::GetInstance()->IsAppLocaleRTL());
+ mozilla::Preferences::SetInt("intl.uidirection", -1);
+}
diff --git a/intl/locale/tests/gtest/TestLocaleServiceNegotiate.cpp b/intl/locale/tests/gtest/TestLocaleServiceNegotiate.cpp
new file mode 100644
index 0000000000..c428e81c8d
--- /dev/null
+++ b/intl/locale/tests/gtest/TestLocaleServiceNegotiate.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/intl/LocaleService.h"
+
+using namespace mozilla::intl;
+
+TEST(Intl_Locale_LocaleService, Negotiate)
+{
+ nsTArray<nsCString> requestedLocales;
+ nsTArray<nsCString> availableLocales;
+ nsTArray<nsCString> supportedLocales;
+ nsAutoCString defaultLocale("en-US");
+ int32_t strategy = LocaleService::kLangNegStrategyFiltering;
+
+ requestedLocales.AppendElement("sr"_ns);
+
+ availableLocales.AppendElement("sr-Cyrl"_ns);
+ availableLocales.AppendElement("sr-Latn"_ns);
+
+ LocaleService::GetInstance()->NegotiateLanguages(
+ requestedLocales, availableLocales, defaultLocale, strategy,
+ supportedLocales);
+
+ ASSERT_TRUE(supportedLocales.Length() == 2);
+ ASSERT_TRUE(supportedLocales[0].EqualsLiteral("sr-Cyrl"));
+ ASSERT_TRUE(supportedLocales[1].EqualsLiteral("en-US"));
+}
+
+TEST(Intl_Locale_LocaleService, UseLSDefaultLocale)
+{
+ nsTArray<nsCString> requestedLocales;
+ nsTArray<nsCString> availableLocales;
+ nsTArray<nsCString> supportedLocales;
+ nsAutoCString defaultLocale("en-US");
+ int32_t strategy = LocaleService::kLangNegStrategyLookup;
+
+ requestedLocales.AppendElement("sr"_ns);
+
+ availableLocales.AppendElement("de"_ns);
+
+ LocaleService::GetInstance()->NegotiateLanguages(
+ requestedLocales, availableLocales, defaultLocale, strategy,
+ supportedLocales);
+
+ nsAutoCString lsDefaultLocale;
+ LocaleService::GetInstance()->GetDefaultLocale(lsDefaultLocale);
+ ASSERT_TRUE(supportedLocales.Length() == 1);
+ ASSERT_TRUE(supportedLocales[0].Equals(lsDefaultLocale));
+}
diff --git a/intl/locale/tests/gtest/TestMozLocale.cpp b/intl/locale/tests/gtest/TestMozLocale.cpp
new file mode 100644
index 0000000000..c56bc14eb3
--- /dev/null
+++ b/intl/locale/tests/gtest/TestMozLocale.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/intl/MozLocale.h"
+
+using namespace mozilla::intl;
+
+TEST(Intl_Locale_Locale, Locale)
+{
+ Locale loc = Locale("en-US");
+
+ ASSERT_TRUE(loc.GetLanguage().Equals("en"));
+ ASSERT_TRUE(loc.GetRegion().Equals("US"));
+}
+
+TEST(Intl_Locale_Locale, AsString)
+{
+ Locale loc = Locale("ja-jp-windows");
+
+ ASSERT_TRUE(loc.AsString().Equals("ja-JP-windows"));
+}
+
+TEST(Intl_Locale_Locale, GetSubTags)
+{
+ Locale loc = Locale("en-latn-us-macos");
+
+ ASSERT_TRUE(loc.GetLanguage().Equals("en"));
+ ASSERT_TRUE(loc.GetScript().Equals("Latn"));
+ ASSERT_TRUE(loc.GetRegion().Equals("US"));
+
+ nsTArray<nsCString> variants;
+ loc.GetVariants(variants);
+ ASSERT_TRUE(variants.Length() == 1);
+ ASSERT_TRUE(variants[0].Equals("macos"));
+}
+
+TEST(Intl_Locale_Locale, Matches)
+{
+ Locale loc = Locale("en-US");
+
+ Locale loc2 = Locale("en-GB");
+ ASSERT_FALSE(loc == loc2);
+
+ Locale loc3 = Locale("en-US");
+ ASSERT_TRUE(loc == loc3);
+
+ Locale loc4 = Locale("En_us");
+ ASSERT_TRUE(loc == loc4);
+}
+
+TEST(Intl_Locale_Locale, MatchesRange)
+{
+ Locale loc = Locale("en-US");
+
+ Locale loc2 = Locale("en-Latn-US");
+ ASSERT_FALSE(loc == loc2);
+ ASSERT_TRUE(loc.Matches(loc2, true, false));
+ ASSERT_FALSE(loc.Matches(loc2, false, true));
+ ASSERT_FALSE(loc.Matches(loc2, false, false));
+ ASSERT_TRUE(loc.Matches(loc2, true, true));
+
+ Locale loc3 = Locale("en");
+ ASSERT_FALSE(loc == loc3);
+ ASSERT_TRUE(loc.Matches(loc3, false, true));
+ ASSERT_FALSE(loc.Matches(loc3, true, false));
+ ASSERT_FALSE(loc.Matches(loc3, false, false));
+ ASSERT_TRUE(loc.Matches(loc3, true, true));
+}
+
+TEST(Intl_Locale_Locale, Variants)
+{
+ Locale loc = Locale("en-US-UniFon-BasicEng");
+
+ // Make sure that we canonicalize and sort variant tags
+ ASSERT_TRUE(loc.AsString().Equals("en-US-basiceng-unifon"));
+}
+
+TEST(Intl_Locale_Locale, InvalidLocale)
+{
+ Locale loc = Locale("en-verylongsubtag");
+ ASSERT_FALSE(loc.IsWellFormed());
+
+ Locale loc2 = Locale("p-te");
+ ASSERT_FALSE(loc2.IsWellFormed());
+}
+
+TEST(Intl_Locale_Locale, ClearRegion)
+{
+ Locale loc = Locale("en-US");
+ loc.ClearRegion();
+ ASSERT_TRUE(loc.AsString().Equals("en"));
+}
+
+TEST(Intl_Locale_Locale, ClearVariants)
+{
+ Locale loc = Locale("en-US-windows");
+ loc.ClearVariants();
+ ASSERT_TRUE(loc.AsString().Equals("en-US"));
+}
+
+TEST(Intl_Locale_Locale, jaJPmac)
+{
+ Locale loc = Locale("ja-JP-mac");
+ ASSERT_TRUE(loc.AsString().Equals("ja-JP-macos"));
+}
+
+TEST(Intl_Locale_Locale, Maximize)
+{
+ Locale loc = Locale("en");
+
+ ASSERT_TRUE(loc.GetLanguage().Equals("en"));
+ ASSERT_TRUE(loc.GetScript().IsEmpty());
+ ASSERT_TRUE(loc.GetRegion().IsEmpty());
+
+ ASSERT_TRUE(loc.Maximize());
+
+ ASSERT_TRUE(loc.GetLanguage().Equals("en"));
+ ASSERT_TRUE(loc.GetScript().Equals("Latn"));
+ ASSERT_TRUE(loc.GetRegion().Equals("US"));
+}
diff --git a/intl/locale/tests/gtest/TestOSPreferences.cpp b/intl/locale/tests/gtest/TestOSPreferences.cpp
new file mode 100644
index 0000000000..605b301c09
--- /dev/null
+++ b/intl/locale/tests/gtest/TestOSPreferences.cpp
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/intl/OSPreferences.h"
+
+using namespace mozilla::intl;
+
+/**
+ * We test that on all platforms we test against (irrelevant of the tier),
+ * we will be able to retrieve at least a single locale out of the system.
+ *
+ * In theory, that may not be true, but if we encounter such platform we should
+ * decide how to handle this and special case and this test should make
+ * it not happen without us noticing.
+ */
+TEST(Intl_Locale_OSPreferences, GetSystemLocales)
+{
+ nsTArray<nsCString> systemLocales;
+ ASSERT_TRUE(NS_SUCCEEDED(
+ OSPreferences::GetInstance()->GetSystemLocales(systemLocales)));
+
+ ASSERT_FALSE(systemLocales.IsEmpty());
+}
+
+/**
+ * We test that on all platforms we test against (irrelevant of the tier),
+ * we will be able to retrieve at least a single locale out of the system.
+ *
+ * In theory, that may not be true, but if we encounter such platform we should
+ * decide how to handle this and special case and this test should make
+ * it not happen without us noticing.
+ */
+TEST(Intl_Locale_OSPreferences, GetRegionalPrefsLocales)
+{
+ nsTArray<nsCString> rgLocales;
+ ASSERT_TRUE(NS_SUCCEEDED(
+ OSPreferences::GetInstance()->GetRegionalPrefsLocales(rgLocales)));
+
+ ASSERT_FALSE(rgLocales.IsEmpty());
+}
+
+/**
+ * We test that on all platforms we test against,
+ * we will be able to retrieve a date and time pattern.
+ *
+ * This may come back empty on platforms where we don't have platforms
+ * bindings for, so effectively, we're testing for crashes. We should
+ * never crash.
+ */
+TEST(Intl_Locale_OSPreferences, GetDateTimePattern)
+{
+ nsAutoCString pattern;
+ OSPreferences* osprefs = OSPreferences::GetInstance();
+
+ struct Test {
+ int dateStyle;
+ int timeStyle;
+ const char* locale;
+ };
+ Test tests[] = {{0, 0, ""}, {1, 0, "pl"}, {2, 0, "de-DE"}, {3, 0, "fr"},
+ {4, 0, "ar"},
+
+ {0, 1, ""}, {0, 2, "it"}, {0, 3, ""}, {0, 4, "ru"},
+
+ {4, 1, ""}, {3, 2, "cs"}, {2, 3, ""}, {1, 4, "ja"}};
+
+ for (unsigned i = 0; i < mozilla::ArrayLength(tests); i++) {
+ const Test& t = tests[i];
+ if (NS_SUCCEEDED(osprefs->GetDateTimePattern(
+ t.dateStyle, t.timeStyle, nsDependentCString(t.locale), pattern))) {
+ ASSERT_TRUE((t.dateStyle == 0 && t.timeStyle == 0) || !pattern.IsEmpty());
+ }
+ }
+
+ // If the locale is not specified, we should get the pattern corresponding to
+ // the first regional prefs locale.
+ AutoTArray<nsCString, 10> rpLocales;
+ LocaleService::GetInstance()->GetRegionalPrefsLocales(rpLocales);
+ ASSERT_TRUE(rpLocales.Length() > 0);
+
+ nsAutoCString rpLocalePattern;
+ ASSERT_TRUE(NS_SUCCEEDED(
+ osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleLong,
+ mozIOSPreferences::dateTimeFormatStyleLong,
+ rpLocales[0], rpLocalePattern)));
+ ASSERT_TRUE(NS_SUCCEEDED(
+ osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleLong,
+ mozIOSPreferences::dateTimeFormatStyleLong,
+ nsDependentCString(""), pattern)));
+ ASSERT_EQ(rpLocalePattern, pattern);
+}
+
+/**
+ * Test that is possible to override the OS defaults through a pref.
+ */
+TEST(Intl_Locale_OSPreferences, GetDateTimePatternPrefOverrides)
+{
+ nsresult nr;
+ nsAutoCString default_pattern, pattern;
+ OSPreferences* osprefs = OSPreferences::GetInstance();
+
+ struct {
+ const char* DatePref;
+ const char* TimePref;
+ int32_t DateTimeFormatStyle;
+ } configs[] = {{"intl.date_time.pattern_override.date_short",
+ "intl.date_time.pattern_override.time_short",
+ mozIOSPreferences::dateTimeFormatStyleShort},
+ {"intl.date_time.pattern_override.date_medium",
+ "intl.date_time.pattern_override.time_medium",
+ mozIOSPreferences::dateTimeFormatStyleMedium},
+ {"intl.date_time.pattern_override.date_long",
+ "intl.date_time.pattern_override.time_long",
+ mozIOSPreferences::dateTimeFormatStyleLong},
+ {"intl.date_time.pattern_override.date_full",
+ "intl.date_time.pattern_override.time_full",
+ mozIOSPreferences::dateTimeFormatStyleFull}};
+
+ for (const auto& config : configs) {
+ // Get default value for the OS
+ nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
+ mozIOSPreferences::dateTimeFormatStyleNone,
+ nsDependentCString(""), default_pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+
+ // Override date format
+ mozilla::Preferences::SetCString(config.DatePref, "yy-MM");
+ nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
+ mozIOSPreferences::dateTimeFormatStyleNone,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_TRUE(pattern.EqualsASCII("yy-MM"));
+
+ // Override time format
+ mozilla::Preferences::SetCString(config.TimePref, "HH:mm");
+ nr = osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone,
+ config.DateTimeFormatStyle,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_TRUE(pattern.EqualsASCII("HH:mm"));
+
+ // Override both
+ nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
+ config.DateTimeFormatStyle,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_TRUE(pattern.Find("yy-MM") != kNotFound);
+ ASSERT_TRUE(pattern.Find("HH:mm") != kNotFound);
+
+ // Clear overrides, we should get the default value back.
+ mozilla::Preferences::ClearUser(config.DatePref);
+ mozilla::Preferences::ClearUser(config.TimePref);
+ nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
+ mozIOSPreferences::dateTimeFormatStyleNone,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_EQ(default_pattern, pattern);
+ }
+
+ // Test overriding connector
+ nr = osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort,
+ mozIOSPreferences::dateTimeFormatStyleShort,
+ nsDependentCString(""), default_pattern);
+
+ mozilla::Preferences::SetCString("intl.date_time.pattern_override.date_short",
+ "yyyy-MM-dd");
+ mozilla::Preferences::SetCString("intl.date_time.pattern_override.time_short",
+ "HH:mm:ss");
+ mozilla::Preferences::SetCString(
+ "intl.date_time.pattern_override.date_time_short", "{1} {0}");
+ nr = osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort,
+ mozIOSPreferences::dateTimeFormatStyleShort,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_TRUE(pattern.EqualsASCII("yyyy-MM-dd HH:mm:ss"));
+
+ // Reset to date and time to defaults
+ mozilla::Preferences::ClearUser("intl.date_time.pattern_override.date_short");
+ mozilla::Preferences::ClearUser("intl.date_time.pattern_override.time_short");
+
+ // Invalid patterns are ignored
+ mozilla::Preferences::SetCString(
+ "intl.date_time.pattern_override.date_time_short", "hello, world!");
+ nr = osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort,
+ mozIOSPreferences::dateTimeFormatStyleShort,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_EQ(default_pattern, pattern);
+
+ // Clearing the override results in getting the default pattern back.
+ mozilla::Preferences::ClearUser(
+ "intl.date_time.pattern_override.date_time_short");
+ nr = osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleShort,
+ mozIOSPreferences::dateTimeFormatStyleShort,
+ nsDependentCString(""), pattern);
+ ASSERT_TRUE(NS_SUCCEEDED(nr));
+ ASSERT_EQ(default_pattern, pattern);
+}
diff --git a/intl/locale/tests/gtest/moz.build b/intl/locale/tests/gtest/moz.build
new file mode 100644
index 0000000000..e23c828f26
--- /dev/null
+++ b/intl/locale/tests/gtest/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "TestCollation.cpp",
+ "TestDateTimeFormat.cpp",
+ "TestLocaleService.cpp",
+ "TestLocaleServiceNegotiate.cpp",
+ "TestMozLocale.cpp",
+ "TestOSPreferences.cpp",
+]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/intl/locale/tests/sort/us-ascii_base.txt b/intl/locale/tests/sort/us-ascii_base.txt
new file mode 100644
index 0000000000..3bc7fbc7be
--- /dev/null
+++ b/intl/locale/tests/sort/us-ascii_base.txt
@@ -0,0 +1,95 @@
+
+!
+"
+#
+$
+%
+&
+'
+(
+)
+*
++
+,
+-
+.
+/
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+:
+;
+<
+=
+>
+?
+@
+A
+B
+C
+D
+E
+F
+G
+H
+I
+J
+K
+L
+M
+N
+O
+P
+Q
+R
+S
+T
+U
+V
+W
+X
+Y
+Z
+[
+\
+]
+^
+_
+`
+a
+b
+c
+d
+e
+f
+g
+h
+i
+j
+k
+l
+m
+n
+o
+p
+q
+r
+s
+t
+u
+v
+w
+x
+y
+z
+{
+|
+}
+~
diff --git a/intl/locale/tests/sort/us-ascii_base_case_res.txt b/intl/locale/tests/sort/us-ascii_base_case_res.txt
new file mode 100644
index 0000000000..3e526d226b
--- /dev/null
+++ b/intl/locale/tests/sort/us-ascii_base_case_res.txt
@@ -0,0 +1,96 @@
+'
+-
+
+!
+"
+#
+$
+%
+&
+(
+)
+*
+,
+.
+/
+:
+;
+?
+@
+[
+\
+]
+^
+_
+`
+{
+|
+}
+~
++
+<
+=
+>
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+a
+A
+b
+B
+c
+C
+d
+D
+e
+E
+f
+F
+g
+G
+h
+H
+i
+I
+j
+J
+k
+K
+l
+L
+m
+M
+n
+N
+o
+O
+p
+P
+q
+Q
+r
+R
+s
+S
+t
+T
+u
+U
+v
+V
+w
+W
+x
+X
+y
+Y
+z
+Z
+
diff --git a/intl/locale/tests/sort/us-ascii_base_nocase_res.txt b/intl/locale/tests/sort/us-ascii_base_nocase_res.txt
new file mode 100644
index 0000000000..2d18096db5
--- /dev/null
+++ b/intl/locale/tests/sort/us-ascii_base_nocase_res.txt
@@ -0,0 +1,96 @@
+'
+-
+
+!
+"
+#
+$
+%
+&
+(
+)
+*
+,
+.
+/
+:
+;
+?
+@
+[
+\
+]
+^
+_
+`
+{
+|
+}
+~
++
+<
+=
+>
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+A
+a
+B
+b
+C
+c
+d
+D
+e
+E
+f
+F
+g
+G
+h
+H
+i
+I
+j
+J
+k
+K
+L
+l
+m
+M
+n
+N
+O
+o
+p
+P
+q
+Q
+r
+R
+s
+S
+t
+T
+u
+U
+v
+V
+w
+W
+X
+x
+Y
+y
+Z
+z
+
diff --git a/intl/locale/tests/sort/us-ascii_sort.txt b/intl/locale/tests/sort/us-ascii_sort.txt
new file mode 100644
index 0000000000..b6962f8de8
--- /dev/null
+++ b/intl/locale/tests/sort/us-ascii_sort.txt
@@ -0,0 +1,78 @@
+ludwig van beethoven
+Ludwig van Beethoven
+Ludwig van beethoven
+Jane
+jane
+JANE
+jAne
+jaNe
+janE
+JAne
+JaNe
+JanE
+JANe
+JaNE
+JAnE
+jANE
+Umberto Eco
+Umberto eco
+umberto eco
+umberto Eco
+UMBERTO ECO
+ace
+bash
+*ace
+!ace
+%ace
+~ace
+#ace
+cork
+denizen
+[denizen]
+(denizen)
+{denizen}
+/denizen/
+#denizen#
+$denizen$
+@denizen
+elf
+full
+gnome
+gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Japanese
+gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Javanese
+hint
+Internationalization
+Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
+Zinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizatio
+n
+Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalization
+ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTioninternationalizationinternationalizationinternationalizationinternationalization
+ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTion
+jostle
+kin
+Laymen
+lumens
+Lumens
+motleycrew
+motley crew
+niven's creative talents
+nivens creative talents
+opie
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rockies
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rokkies
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the rockies
+quilt's
+quilts
+quilt
+Rondo
+street
+tamale oxidization and iodization in rapid progress
+tamale oxidization and iodization in rapid Progress
+until
+vera
+Wobble
+Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoneme
+Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoname
+yearn
+zodiac
+
diff --git a/intl/locale/tests/sort/us-ascii_sort_case_res.txt b/intl/locale/tests/sort/us-ascii_sort_case_res.txt
new file mode 100644
index 0000000000..2720a83426
--- /dev/null
+++ b/intl/locale/tests/sort/us-ascii_sort_case_res.txt
@@ -0,0 +1,79 @@
+!ace
+#ace
+#denizen#
+$denizen$
+%ace
+(denizen)
+*ace
+/denizen/
+@denizen
+[denizen]
+{denizen}
+~ace
+ace
+bash
+cork
+denizen
+elf
+full
+gnome
+gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Japanese
+gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Javanese
+hint
+Internationalization
+jane
+janE
+jaNe
+jAne
+jANE
+Jane
+JanE
+JaNe
+JaNE
+JAne
+JAnE
+JANe
+JANE
+jostle
+kin
+Laymen
+ludwig van beethoven
+Ludwig van beethoven
+Ludwig van Beethoven
+lumens
+Lumens
+motley crew
+motleycrew
+nivens creative talents
+niven's creative talents
+opie
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the rockies
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rockies
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rokkies
+quilt
+quilts
+quilt's
+Rondo
+street
+tamale oxidization and iodization in rapid progress
+tamale oxidization and iodization in rapid Progress
+umberto eco
+umberto Eco
+Umberto eco
+Umberto Eco
+UMBERTO ECO
+until
+veda
+Veda
+vera
+Vera
+Wobble
+Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoname
+Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoneme
+yearn
+Zinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
+Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalization
+Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
+ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTion
+ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTioninternationalizationinternationalizationinternationalizationinternationalization
+zodiac
diff --git a/intl/locale/tests/sort/us-ascii_sort_nocase_res.txt b/intl/locale/tests/sort/us-ascii_sort_nocase_res.txt
new file mode 100644
index 0000000000..fa3426d4e9
--- /dev/null
+++ b/intl/locale/tests/sort/us-ascii_sort_nocase_res.txt
@@ -0,0 +1,79 @@
+!ace
+#ace
+#denizen#
+$denizen$
+%ace
+(denizen)
+*ace
+/denizen/
+@denizen
+[denizen]
+{denizen}
+~ace
+ace
+bash
+cork
+denizen
+elf
+full
+gnome
+gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Japanese
+gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Javanese
+hint
+Internationalization
+JANe
+jANE
+JAnE
+JaNE
+Jane
+JanE
+JaNe
+JAne
+janE
+jaNe
+jAne
+JANE
+jane
+jostle
+kin
+Laymen
+Ludwig van beethoven
+Ludwig van Beethoven
+ludwig van beethoven
+Lumens
+lumens
+motley crew
+motleycrew
+nivens creative talents
+niven's creative talents
+opie
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the rockies
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rockies
+posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rokkies
+quilt
+quilts
+quilt's
+Rondo
+street
+tamale oxidization and iodization in rapid Progress
+tamale oxidization and iodization in rapid progress
+Umberto eco
+Umberto Eco
+umberto eco
+UMBERTO ECO
+umberto Eco
+until
+Veda
+veda
+Vera
+vera
+Wobble
+Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoname
+Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoneme
+yearn
+Zinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
+Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalization
+ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTioninternationalizationinternationalizationinternationalizationinternationalization
+ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTion
+Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
+zodiac
diff --git a/intl/locale/tests/unit/data/chrome.manifest b/intl/locale/tests/unit/data/chrome.manifest
new file mode 100644
index 0000000000..a8678deef3
--- /dev/null
+++ b/intl/locale/tests/unit/data/chrome.manifest
@@ -0,0 +1 @@
+content locale ./
diff --git a/intl/locale/tests/unit/data/intl_on_workers_worker.js b/intl/locale/tests/unit/data/intl_on_workers_worker.js
new file mode 100644
index 0000000000..fe03359002
--- /dev/null
+++ b/intl/locale/tests/unit/data/intl_on_workers_worker.js
@@ -0,0 +1,4 @@
+self.onmessage = function(data) {
+ let myLocale = Intl.NumberFormat().resolvedOptions().locale;
+ self.postMessage(myLocale);
+};
diff --git a/intl/locale/tests/unit/test_bug1086527.js b/intl/locale/tests/unit/test_bug1086527.js
new file mode 100644
index 0000000000..a344688698
--- /dev/null
+++ b/intl/locale/tests/unit/test_bug1086527.js
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This unit test makes sure that PluralForm.get can be called from strict mode
+ */
+
+const { PluralForm } = ChromeUtils.import(
+ "resource://gre/modules/PluralForm.jsm"
+);
+
+delete PluralForm.numForms;
+delete PluralForm.get;
+[PluralForm.get, PluralForm.numForms] = PluralForm.makeGetter(9);
+
+function run_test() {
+ "use strict";
+
+ Assert.equal(3, PluralForm.numForms());
+ Assert.equal("one", PluralForm.get(5, "one;many"));
+}
diff --git a/intl/locale/tests/unit/test_bug22310.js b/intl/locale/tests/unit/test_bug22310.js
new file mode 100644
index 0000000000..bc312398db
--- /dev/null
+++ b/intl/locale/tests/unit/test_bug22310.js
@@ -0,0 +1,65 @@
+String.prototype.has = function(s) {
+ return this.includes(s);
+};
+
+function dt(locale) {
+ var date = new Date("2008-06-30T13:56:34");
+ const dtOptions = {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ second: "numeric",
+ };
+ return date.toLocaleString(locale, dtOptions);
+}
+
+var all_passed = true;
+const tests = [
+ [dt("en-US").has("June"), "month name in en-US"],
+ [dt("en-US").has("2008"), "year in en-US"],
+ [dt("da").has("jun"), "month name in da"],
+ [dt("da-DK") == dt("da"), "da same as da-DK"],
+ [
+ dt("en-GB").has("30") &&
+ dt("en-GB").has("June") &&
+ dt("en-GB").indexOf("30") < dt("en-GB").indexOf("June"),
+ "day before month in en-GB",
+ ],
+ [
+ dt("en-US").has("30") &&
+ dt("en-US").has("June") &&
+ dt("en-US").indexOf("30") > dt("en-US").indexOf("June"),
+ "month before day in en-US",
+ ],
+ [dt("ja-JP").has("\u5E746\u670830\u65E5"), "year month and day in ja-JP"],
+ // The Firefox locale code ja-JP-mac will be resolved to a BCP47-compliant
+ // tag ja-JP-x-lvariant-mac by uloc_toLanguageTag
+ [
+ dt("ja-JP") == dt("ja-JP-x-lvariant-mac"),
+ "ja-JP-x-lvariant-mac same as ja-JP",
+ ],
+ [dt("nn-NO").has("juni"), "month name in nn-NO"],
+ [dt("nb-NO").has("juni"), "month name in nb-NO"],
+ // Bug 1261775 - failures on win10
+ //[dt("no-NO").has("30. juni"), "month name in no-NO"],
+ [dt("sv-SE").has("30 jun"), "month name in sv-SE"],
+ [dt("kok").has("\u091C\u0942\u0928"), "month name in kok"],
+ [dt("ta-IN").has("\u0B9C\u0BC2\u0BA9\u0BCD"), "month name in ta-IN"],
+ [!!dt("ab-CD").length, "fallback for ab-CD"],
+];
+
+function one_test(testcase, msg) {
+ if (!testcase) {
+ all_passed = false;
+ dump("Unexpected date format: " + msg + "\n");
+ }
+}
+
+function run_test() {
+ for (var i = 0; i < tests.length; ++i) {
+ one_test(tests[i][0], tests[i][1]);
+ }
+ Assert.ok(all_passed);
+}
diff --git a/intl/locale/tests/unit/test_intl_on_workers.js b/intl/locale/tests/unit/test_intl_on_workers.js
new file mode 100644
index 0000000000..4de22cf1c3
--- /dev/null
+++ b/intl/locale/tests/unit/test_intl_on_workers.js
@@ -0,0 +1,29 @@
+function run_test() {
+ do_load_manifest("data/chrome.manifest");
+
+ if (typeof Intl !== "object") {
+ dump("Intl not enabled, skipping test\n");
+ equal(true, true);
+ return;
+ }
+
+ let mainThreadLocale = Intl.NumberFormat().resolvedOptions().locale;
+ let testWorker = new Worker(
+ "chrome://locale/content/intl_on_workers_worker.js"
+ );
+ testWorker.onmessage = function(e) {
+ try {
+ let workerLocale = e.data;
+ equal(
+ mainThreadLocale,
+ workerLocale,
+ "Worker should inherit Intl locale from main thread."
+ );
+ } finally {
+ do_test_finished();
+ }
+ };
+
+ do_test_pending();
+ testWorker.postMessage("go!");
+}
diff --git a/intl/locale/tests/unit/test_localeService.js b/intl/locale/tests/unit/test_localeService.js
new file mode 100644
index 0000000000..2ad0f82a34
--- /dev/null
+++ b/intl/locale/tests/unit/test_localeService.js
@@ -0,0 +1,241 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].getService(
+ Ci.mozIOSPreferences
+);
+
+const localeService = Services.locale;
+
+/**
+ * Make sure the locale service can be instantiated.
+ */
+
+add_test(function test_defaultLocale() {
+ const defaultLocale = localeService.defaultLocale;
+ Assert.ok(defaultLocale.length !== 0, "Default locale is not empty");
+ run_next_test();
+});
+
+add_test(function test_lastFallbackLocale() {
+ const lastFallbackLocale = localeService.lastFallbackLocale;
+ Assert.ok(lastFallbackLocale === "en-US", "Last fallback locale is en-US");
+ run_next_test();
+});
+
+add_test(function test_appLocalesAsLangTags() {
+ const appLocale = localeService.appLocaleAsLangTag;
+ Assert.ok(appLocale != "", "appLocale is non-empty");
+
+ const appLocales = localeService.appLocalesAsLangTags;
+ Assert.ok(Array.isArray(appLocales), "appLocales returns an array");
+
+ Assert.ok(
+ appLocale == appLocales[0],
+ "appLocale matches first entry in appLocales"
+ );
+
+ const enUSLocales = appLocales.filter(loc => loc === "en-US");
+ Assert.ok(enUSLocales.length == 1, "en-US is present exactly one time");
+
+ run_next_test();
+});
+
+const PREF_REQUESTED_LOCALES = "intl.locale.requested";
+const REQ_LOC_CHANGE_EVENT = "intl:requested-locales-changed";
+
+add_test(function test_requestedLocales() {
+ const requestedLocales = localeService.requestedLocales;
+ Assert.ok(
+ Array.isArray(requestedLocales),
+ "requestedLocales returns an array"
+ );
+
+ run_next_test();
+});
+
+/**
+ * In this test we verify that after we set an observer on the LocaleService
+ * event for requested locales change, it will be fired when the
+ * pref for matchOS is set to true.
+ *
+ * Then, we test that when the matchOS is set to true, we will retrieve
+ * OS locale from requestedLocales.
+ */
+add_test(function test_requestedLocales_matchOS() {
+ do_test_pending();
+
+ Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "ar-IR");
+
+ const observer = {
+ observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case REQ_LOC_CHANGE_EVENT:
+ const reqLocs = localeService.requestedLocales;
+ Assert.ok(reqLocs[0] === osPrefs.systemLocale);
+ Services.obs.removeObserver(observer, REQ_LOC_CHANGE_EVENT);
+ do_test_finished();
+ }
+ },
+ };
+
+ Services.obs.addObserver(observer, REQ_LOC_CHANGE_EVENT);
+ Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "");
+
+ run_next_test();
+});
+
+/**
+ * In this test we verify that after we set an observer on the LocaleService
+ * event for requested locales change, it will be fired when the
+ * pref for browser UI locale changes.
+ */
+add_test(function test_requestedLocales_onChange() {
+ do_test_pending();
+
+ Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "ar-IR");
+
+ const observer = {
+ observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case REQ_LOC_CHANGE_EVENT:
+ const reqLocs = localeService.requestedLocales;
+ Assert.ok(reqLocs[0] === "sr-RU");
+ Services.obs.removeObserver(observer, REQ_LOC_CHANGE_EVENT);
+ do_test_finished();
+ }
+ },
+ };
+
+ Services.obs.addObserver(observer, REQ_LOC_CHANGE_EVENT);
+ Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "sr-RU");
+
+ run_next_test();
+});
+
+add_test(function test_requestedLocale() {
+ Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "tlh");
+
+ let requestedLocale = localeService.requestedLocale;
+ Assert.ok(
+ requestedLocale === "tlh",
+ "requestedLocale returns the right value"
+ );
+
+ Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);
+
+ run_next_test();
+});
+
+add_test(function test_requestedLocales() {
+ localeService.requestedLocales = ["de-AT", "de-DE", "de-CH"];
+
+ let locales = localeService.requestedLocales;
+ Assert.ok(locales[0] === "de-AT");
+ Assert.ok(locales[1] === "de-DE");
+ Assert.ok(locales[2] === "de-CH");
+
+ run_next_test();
+});
+
+add_test(function test_isAppLocaleRTL() {
+ Assert.ok(typeof localeService.isAppLocaleRTL === "boolean");
+
+ run_next_test();
+});
+
+add_test(function test_isAppLocaleRTL_pseudo() {
+ let avLocales = localeService.availableLocales;
+ let reqLocales = localeService.requestedLocales;
+
+ localeService.availableLocales = ["en-US"];
+ localeService.requestedLocales = ["en-US"];
+ Services.prefs.setCharPref("intl.l10n.pseudo", "");
+
+ Assert.ok(localeService.isAppLocaleRTL === false);
+
+ Services.prefs.setCharPref("intl.l10n.pseudo", "bidi");
+ Assert.ok(localeService.isAppLocaleRTL === true);
+
+ Services.prefs.setCharPref("intl.l10n.pseudo", "accented");
+ Assert.ok(localeService.isAppLocaleRTL === false);
+
+ // Clean up
+ localeService.availableLocales = avLocales;
+ localeService.requestedLocales = reqLocales;
+ Services.prefs.clearUserPref("intl.l10n.pseudo");
+
+ run_next_test();
+});
+
+add_test(function test_packagedLocales() {
+ const locales = localeService.packagedLocales;
+ Assert.ok(locales.length !== 0, "Packaged locales are empty");
+ run_next_test();
+});
+
+add_test(function test_availableLocales() {
+ const avLocales = localeService.availableLocales;
+ localeService.availableLocales = ["und", "ar-IR"];
+
+ let locales = localeService.availableLocales;
+ Assert.ok(locales.length == 2);
+ Assert.ok(locales[0] === "und");
+ Assert.ok(locales[1] === "ar-IR");
+
+ localeService.availableLocales = avLocales;
+
+ run_next_test();
+});
+
+/**
+ * This test verifies that all values coming from the pref are sanitized.
+ */
+add_test(function test_requestedLocales_sanitize() {
+ Services.prefs.setStringPref(
+ PREF_REQUESTED_LOCALES,
+ "de,2,#$@#,pl,ąó,!a2,DE-at,,;"
+ );
+
+ let locales = localeService.requestedLocales;
+ Assert.equal(locales[0], "de");
+ Assert.equal(locales[1], "pl");
+ Assert.equal(locales[2], "de-AT");
+ Assert.equal(locales.length, 3);
+
+ Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);
+
+ run_next_test();
+});
+
+add_test(function test_handle_ja_JP_mac() {
+ const bkpAvLocales = localeService.availableLocales;
+
+ localeService.availableLocales = ["ja-JP-mac", "en-US"];
+
+ localeService.requestedLocales = ["ja-JP-mac"];
+
+ let reqLocales = localeService.requestedLocales;
+ Assert.equal(reqLocales[0], "ja-JP-macos");
+
+ let avLocales = localeService.availableLocales;
+ Assert.equal(avLocales[0], "ja-JP-macos");
+
+ let appLocales = localeService.appLocalesAsBCP47;
+ Assert.equal(appLocales[0], "ja-JP-macos");
+
+ let appLocalesAsLT = localeService.appLocalesAsLangTags;
+ Assert.equal(appLocalesAsLT[0], "ja-JP-mac");
+
+ Assert.equal(localeService.appLocaleAsLangTag, "ja-JP-mac");
+
+ localeService.availableLocales = bkpAvLocales;
+
+ run_next_test();
+});
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);
+});
diff --git a/intl/locale/tests/unit/test_localeService_negotiateLanguages.js b/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
new file mode 100644
index 0000000000..392a2dd7d9
--- /dev/null
+++ b/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
@@ -0,0 +1,205 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const localeService = Services.locale;
+
+const data = {
+ filtering: {
+ "exact match": [
+ [["en"], ["en"], ["en"]],
+ [["en-US"], ["en-US"], ["en-US"]],
+ [["en-Latn-US"], ["en-Latn-US"], ["en-Latn-US"]],
+ [["en-Latn-US-windows"], ["en-Latn-US-windows"], ["en-Latn-US-windows"]],
+ [["fr-FR"], ["de", "it", "fr-FR"], ["fr-FR"]],
+ [
+ ["fr", "pl", "de-DE"],
+ ["pl", "en-US", "de-DE"],
+ ["pl", "de-DE"],
+ ],
+ ],
+ "available as range": [
+ [["en-US"], ["en"], ["en"]],
+ [["en-Latn-US"], ["en-US"], ["en-US"]],
+ [["en-US-windows"], ["en-US"], ["en-US"]],
+ [
+ ["fr-CA", "de-DE"],
+ ["fr", "it", "de"],
+ ["fr", "de"],
+ ],
+ [["ja-JP-windows"], ["ja"], ["ja"]],
+ [
+ ["en-Latn-GB", "en-Latn-IN"],
+ ["en-IN", "en-GB"],
+ ["en-GB", "en-IN"],
+ ],
+ ],
+ "should match on likely subtag": [
+ [["en"], ["en-GB", "de", "en-US"], ["en-US", "en-GB"]],
+ [
+ ["en"],
+ ["en-Latn-GB", "de", "en-Latn-US"],
+ ["en-Latn-US", "en-Latn-GB"],
+ ],
+ [["fr"], ["fr-CA", "fr-FR"], ["fr-FR", "fr-CA"]],
+ [["az-IR"], ["az-Latn", "az-Arab"], ["az-Arab"]],
+ [["sr-RU"], ["sr-Cyrl", "sr-Latn"], ["sr-Latn"]],
+ [["sr"], ["sr-Latn", "sr-Cyrl"], ["sr-Cyrl"]],
+ [["zh-GB"], ["zh-Hans", "zh-Hant"], ["zh-Hant"]],
+ [["sr", "ru"], ["sr-Latn", "ru"], ["ru"]],
+ [["sr-RU"], ["sr-Latn-RO", "sr-Cyrl"], ["sr-Latn-RO"]],
+ ],
+ "should match likelySubtag region over other regions": [
+ [["en-CA"], ["en-ZA", "en-GB", "en-US"], ["en-US", "en-ZA", "en-GB"]],
+ ],
+ "should match cross-region": [
+ [["en"], ["en-US"], ["en-US"]],
+ [["en-US"], ["en-GB"], ["en-GB"]],
+ [["en-Latn-US"], ["en-Latn-GB"], ["en-Latn-GB"]],
+ ],
+ "should match cross-variant": [
+ [["en-US-linux"], ["en-US-windows"], ["en-US-windows"]],
+ ],
+ "should prioritize properly": [
+ // exact match first
+ [
+ ["en-US"],
+ ["en-US-windows", "en", "en-US"],
+ ["en-US", "en", "en-US-windows"],
+ ],
+ // available as range second
+ [["en-Latn-US"], ["en-GB", "en-US"], ["en-US", "en-GB"]],
+ // likely subtags third
+ [["en"], ["en-Cyrl-US", "en-Latn-US"], ["en-Latn-US"]],
+ // variant range fourth
+ [
+ ["en-US-macos"],
+ ["en-US-windows", "en-GB-macos"],
+ ["en-US-windows", "en-GB-macos"],
+ ],
+ // regional range fifth
+ [["en-US-macos"], ["en-GB-windows"], ["en-GB-windows"]],
+ [["en-US"], ["en-GB", "en"], ["en", "en-GB"]],
+ [
+ ["fr-CA-macos", "de-DE"],
+ ["de-DE", "fr-FR-windows"],
+ ["fr-FR-windows", "de-DE"],
+ ],
+ ],
+ "should handle default locale properly": [
+ [["fr"], ["de", "it"], []],
+ [["fr"], ["de", "it"], "en-US", ["en-US"]],
+ [["fr"], ["de", "en-US"], "en-US", ["en-US"]],
+ [
+ ["fr", "de-DE"],
+ ["de-DE", "fr-CA"],
+ "en-US",
+ ["fr-CA", "de-DE", "en-US"],
+ ],
+ ],
+ "should handle all matches on the 1st higher than any on the 2nd": [
+ [
+ ["fr-CA-macos", "de-DE"],
+ ["de-DE", "fr-FR-windows"],
+ ["fr-FR-windows", "de-DE"],
+ ],
+ ],
+ "should handle cases and underscores, returning the form given in the 'available' list": [
+ [["fr_FR"], ["fr-FR"], ["fr-FR"]],
+ [["fr_fr"], ["fr-FR"], ["fr-FR"]],
+ [["fr_Fr"], ["fr-fR"], ["fr-fR"]],
+ [["fr_lAtN_fr"], ["fr-Latn-FR"], ["fr-Latn-FR"]],
+ [["fr_FR"], ["fr_FR"], ["fr_FR"]],
+ [["fr-FR"], ["fr_FR"], ["fr_FR"]],
+ [["fr_Cyrl_FR_macos"], ["fr_Cyrl_fr-macos"], ["fr_Cyrl_fr-macos"]],
+ ],
+ "should handle mozilla specific 3-letter variants": [
+ [
+ ["ja-JP-mac", "de-DE"],
+ ["ja-JP-mac", "de-DE"],
+ ["ja-JP-mac", "de-DE"],
+ ],
+ ],
+ "should not crash on invalid input": [
+ [["fą-FŻ"], ["ór_Fń"], []],
+ [["2"], ["ąóżł"], []],
+ [[[""]], ["fr-FR"], []],
+ ],
+ "should not match on invalid input": [[["en"], ["ťŮ"], []]],
+ },
+ matching: {
+ "should match only one per requested": [
+ [
+ ["fr", "en"],
+ ["en-US", "fr-FR", "en", "fr"],
+ null,
+ localeService.langNegStrategyMatching,
+ ["fr", "en"],
+ ],
+ ],
+ },
+ lookup: {
+ "should match only one": [
+ [
+ ["fr-FR", "en"],
+ ["en-US", "fr-FR", "en", "fr"],
+ "en-US",
+ localeService.langNegStrategyLookup,
+ ["fr-FR"],
+ ],
+ ],
+ },
+};
+
+function run_test() {
+ const nl = localeService.negotiateLanguages;
+
+ const json = JSON.stringify;
+ for (const strategy in data) {
+ for (const groupName in data[strategy]) {
+ const group = data[strategy][groupName];
+ for (const test of group) {
+ const requested = test[0];
+ const available = test[1];
+ const defaultLocale = test.length > 3 ? test[2] : undefined;
+ const strategyInner = test.length > 4 ? test[3] : undefined;
+ const supported = test[test.length - 1];
+
+ const result = nl(test[0], test[1], defaultLocale, strategyInner);
+ deepEqual(
+ result,
+ supported,
+ `\nExpected ${json(requested)} * ${json(available)} = ${json(
+ supported
+ )}.\n`
+ );
+ }
+ }
+ }
+
+ // Verify that we error out when requested or available is not an array.
+ for (const [req, avail] in [
+ [null, ["fr-FR"]],
+ [undefined, ["fr-FR"]],
+ [2, ["fr-FR"]],
+ ["fr-FR", ["fr-FR"]],
+ [["fr-FR"], null],
+ [["fr-FR"], undefined],
+ [["fr-FR"], 2],
+ [["fr-FR"], "fr-FR"],
+ [[null], []],
+ [[undefined], []],
+ [[undefined], [null]],
+ [[undefined], [undefined]],
+ [[null], [null]],
+ [[[]], [[2]]],
+ ]) {
+ Assert.throws(
+ () => {
+ nl(req, avail);
+ },
+ err => err.result == Cr.NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY
+ );
+ }
+}
diff --git a/intl/locale/tests/unit/test_osPreferences.js b/intl/locale/tests/unit/test_osPreferences.js
new file mode 100644
index 0000000000..6d1bb637ff
--- /dev/null
+++ b/intl/locale/tests/unit/test_osPreferences.js
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ const osprefs = Cc["@mozilla.org/intl/ospreferences;1"].getService(
+ Ci.mozIOSPreferences
+ );
+
+ const systemLocale = osprefs.systemLocale;
+ Assert.ok(systemLocale != "", "systemLocale is non-empty");
+
+ const systemLocales = osprefs.systemLocales;
+ Assert.ok(Array.isArray(systemLocales), "systemLocales returns an array");
+
+ Assert.ok(
+ systemLocale == systemLocales[0],
+ "systemLocale matches first entry in systemLocales"
+ );
+
+ const rgLocales = osprefs.regionalPrefsLocales;
+ Assert.ok(Array.isArray(rgLocales), "regionalPrefsLocales returns an array");
+
+ const getDateTimePatternTests = [
+ [osprefs.dateTimeFormatStyleNone, osprefs.dateTimeFormatStyleNone, ""],
+ [osprefs.dateTimeFormatStyleShort, osprefs.dateTimeFormatStyleNone, ""],
+ [osprefs.dateTimeFormatStyleNone, osprefs.dateTimeFormatStyleLong, "ar"],
+ [osprefs.dateTimeFormatStyleFull, osprefs.dateTimeFormatStyleMedium, "ru"],
+ ];
+
+ for (let i = 0; i < getDateTimePatternTests.length; i++) {
+ const test = getDateTimePatternTests[i];
+
+ const pattern = osprefs.getDateTimePattern(...test);
+ if (
+ test[0] !== osprefs.dateTimeFormatStyleNone &&
+ test[1] !== osprefs.dateTImeFormatStyleNone
+ ) {
+ Assert.greater(pattern.length, 0, "pattern is not empty.");
+ }
+ }
+
+ Assert.ok(1, "osprefs didn't crash");
+}
diff --git a/intl/locale/tests/unit/test_pluralForm.js b/intl/locale/tests/unit/test_pluralForm.js
new file mode 100644
index 0000000000..a5c486327c
--- /dev/null
+++ b/intl/locale/tests/unit/test_pluralForm.js
@@ -0,0 +1,6150 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Make sure each of the plural forms have the correct number of forms and
+ * match up in functionality.
+ */
+
+const { PluralForm } = ChromeUtils.import(
+ "resource://gre/modules/PluralForm.jsm"
+);
+
+function run_test() {
+ let allExpect = [
+ [
+ // 0: Chinese 0-9, 10-19, ..., 90-99
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ // 100-109, 110-119, ..., 190-199
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ // 200-209, 210-219, ..., 290-299
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ ],
+ [
+ // 1: English 0-9, 10-19, ..., 90-99
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 100-109, 110-119, ..., 190-199
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 200-209, 210-219, ..., 290-299
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ ],
+ [
+ // 2: French 0-9, 10-19, ..., 90-99
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 100-109, 110-119, ..., 190-199
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 200-209, 210-219, ..., 290-299
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ ],
+ [
+ // 3: Latvian 0-9, 10-19, ..., 90-99
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 4: Scottish Gaelic 0-9, 10-19, ..., 90-99
+ 4,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 100-109, 110-119, ..., 190-199
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 200-209, 210-219, ..., 290-299
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ ],
+ [
+ // 5: Romanian 0-9, 10-19, ..., 90-99
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 6: Lithuanian 0-9, 10-19, ..., 90-99
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 7: Russian 0-9, 10-19, ..., 90-99
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 8: Slovak 0-9, 10-19, ..., 90-99
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 9: Polish 0-9, 10-19, ..., 90-99
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 10: Slovenian 0-9, 10-19, ..., 90-99
+ 4,
+ 1,
+ 2,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 100-109, 110-119, ..., 190-199
+ 4,
+ 1,
+ 2,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 200-209, 210-219, ..., 290-299
+ 4,
+ 1,
+ 2,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ ],
+ [
+ // 11: Irish Gaeilge 0-9, 10-19, ..., 90-99
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ // 100-109, 110-119, ..., 190-199
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ // 200-209, 210-219, ..., 290-299
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ ],
+ [
+ // 12: Arabic 0-9, 10-19, ..., 90-99
+ 6,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 100-109, 110-119, ..., 190-199
+ 5,
+ 5,
+ 5,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 200-209, 210-219, ..., 290-299
+ 5,
+ 5,
+ 5,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ ],
+ [
+ // 13: Maltese 0-9, 10-19, ..., 90-99
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 100-109, 110-119, ..., 190-199
+ 4,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ // 200-209, 210-219, ..., 290-299
+ 4,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ ],
+ [
+ // 14: Unused 0-9, 10-19, ..., 90-99
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ [
+ // 15: Icelandic, Macedonian 0-9, 10-19, ..., 90-99
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 100-109, 110-119, ..., 190-199
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 200-209, 210-219, ..., 290-299
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ ],
+ [
+ // 16: Breton 0-9, 10-19, ..., 90-99
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ // 100-109, 110-119, ..., 190-199
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ // 200-209, 210-219, ..., 290-299
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 1,
+ 2,
+ 3,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 3,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ ],
+ [
+ // 17: Shuar 0-9, 10-19, ..., 90-99
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 100-109, 110-119, ..., 190-199
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ // 200-209, 210-219, ..., 290-299
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ ],
+ [
+ // 18: Welsh 0-9, 10-19, ..., 90-99
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 6,
+ 5,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ // 100-109, 110-119, ..., 190-199
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ // 200-209, 210-219, ..., 290-299
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ ],
+ [
+ // 19: Slavic languages (bs, hr, sr) 0-9, 10-19, ..., 90-99
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 100-109, 110-119, ..., 190-199
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ // 200-209, 210-219, ..., 290-299
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ ],
+ ];
+
+ for (let [rule, expect] of allExpect.entries()) {
+ print("\nTesting rule #" + rule);
+
+ let [get, numForms] = PluralForm.makeGetter(rule);
+
+ // Make sure the largest value expected matches the number of plural forms
+ let maxExpect = Math.max.apply(this, expect);
+ Assert.equal(maxExpect, numForms());
+
+ // Make a string of numbers, e.g., 1;2;3;4;5
+ let words = [];
+ for (let i = 1; i <= maxExpect; i++) {
+ words.push(i);
+ }
+ words = words.join(";");
+
+ // Make sure we get the expected number
+ for (let [index, number] of expect.entries()) {
+ print(
+ [
+ "Plural form of ",
+ index,
+ " should be ",
+ number,
+ " (",
+ words,
+ ")",
+ ].join("")
+ );
+ Assert.equal(get(index, words), number);
+ }
+ }
+}
diff --git a/intl/locale/tests/unit/test_pluralForm_english.js b/intl/locale/tests/unit/test_pluralForm_english.js
new file mode 100644
index 0000000000..1e4cbf12ab
--- /dev/null
+++ b/intl/locale/tests/unit/test_pluralForm_english.js
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This unit test makes sure the plural form for the default language (by
+ * development), English, is working for the PluralForm javascript module.
+ */
+
+const { PluralForm } = ChromeUtils.import(
+ "resource://gre/modules/PluralForm.jsm"
+);
+
+function run_test() {
+ // English has 2 plural forms
+ Assert.equal(2, PluralForm.numForms());
+
+ // Make sure for good inputs, things work as expected
+ for (var num = 0; num <= 200; num++) {
+ Assert.equal(
+ num == 1 ? "word" : "words",
+ PluralForm.get(num, "word;words")
+ );
+ }
+
+ // Not having enough plural forms defaults to the first form
+ Assert.equal("word", PluralForm.get(2, "word"));
+
+ // Empty forms defaults to the first form
+ Assert.equal("word", PluralForm.get(2, "word;"));
+}
diff --git a/intl/locale/tests/unit/test_pluralForm_makeGetter.js b/intl/locale/tests/unit/test_pluralForm_makeGetter.js
new file mode 100644
index 0000000000..a5478d38e6
--- /dev/null
+++ b/intl/locale/tests/unit/test_pluralForm_makeGetter.js
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This unit test makes sure the plural form for Irish Gaeilge is working by
+ * using the makeGetter method instead of using the default language (by
+ * development), English.
+ */
+
+const { PluralForm } = ChromeUtils.import(
+ "resource://gre/modules/PluralForm.jsm"
+);
+
+function run_test() {
+ // Irish is plural rule #11
+ let [get, numForms] = PluralForm.makeGetter(11);
+
+ // Irish has 5 plural forms
+ Assert.equal(5, numForms());
+
+ // I don't really know Irish, so I'll stick in some dummy text
+ let words = "is 1;is 2;is 3-6;is 7-10;everything else";
+
+ let test = function(text, low, high) {
+ for (let num = low; num <= high; num++) {
+ Assert.equal(text, get(num, words));
+ }
+ };
+
+ // Make sure for good inputs, things work as expected
+ test("everything else", 0, 0);
+ test("is 1", 1, 1);
+ test("is 2", 2, 2);
+ test("is 3-6", 3, 6);
+ test("is 7-10", 7, 10);
+ test("everything else", 11, 200);
+}
diff --git a/intl/locale/tests/unit/xpcshell.ini b/intl/locale/tests/unit/xpcshell.ini
new file mode 100644
index 0000000000..75ab013434
--- /dev/null
+++ b/intl/locale/tests/unit/xpcshell.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+head =
+support-files =
+ data/intl_on_workers_worker.js
+ data/chrome.manifest
+
+[test_bug22310.js]
+skip-if = toolkit != "windows" && toolkit != "cocoa"
+
+[test_bug1086527.js]
+[test_intl_on_workers.js]
+skip-if = toolkit == "android" # bug 1309447
+
+[test_pluralForm.js]
+[test_pluralForm_english.js]
+[test_pluralForm_makeGetter.js]
+
+[test_osPreferences.js]
+skip-if = toolkit == "android" # bug 1344596
+[test_localeService.js]
+[test_localeService_negotiateLanguages.js]