UnicodeKeywords.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #define AK_DONT_REPLACE_STD
  7. #include <AK/QuickSort.h>
  8. #include <AK/ScopeGuard.h>
  9. #include <LibLocale/DateTimeFormat.h>
  10. #include <LibLocale/ICU.h>
  11. #include <LibLocale/UnicodeKeywords.h>
  12. #include <unicode/calendar.h>
  13. #include <unicode/coll.h>
  14. #include <unicode/locid.h>
  15. #include <unicode/numsys.h>
  16. #include <unicode/ucurr.h>
  17. namespace Locale {
  18. template<typename Filter>
  19. static Vector<String> icu_string_enumeration_to_list(OwnPtr<icu::StringEnumeration> enumeration, Filter&& filter)
  20. {
  21. UErrorCode status = U_ZERO_ERROR;
  22. Vector<String> result;
  23. if (!enumeration)
  24. return {};
  25. while (true) {
  26. i32 length = 0;
  27. auto const* keyword = enumeration->next(&length, status);
  28. if (icu_failure(status) || keyword == nullptr)
  29. break;
  30. if (!filter(keyword))
  31. continue;
  32. result.append(MUST(String::from_utf8({ keyword, static_cast<size_t>(length) })));
  33. }
  34. return result;
  35. }
  36. static Vector<String> icu_string_enumeration_to_list(OwnPtr<icu::StringEnumeration> enumeration)
  37. {
  38. return icu_string_enumeration_to_list(move(enumeration), [](char const*) { return true; });
  39. }
  40. Vector<String> available_keyword_values(StringView locale, StringView key)
  41. {
  42. if (key == "ca"sv)
  43. return available_calendars(locale);
  44. if (key == "co"sv)
  45. return available_collations(locale);
  46. if (key == "hc"sv)
  47. return available_hour_cycles(locale);
  48. if (key == "kf"sv)
  49. return available_collation_case_orderings();
  50. if (key == "kn"sv)
  51. return available_collation_numeric_orderings();
  52. if (key == "nu"sv)
  53. return available_number_systems(locale);
  54. TODO();
  55. }
  56. Vector<String> const& available_calendars()
  57. {
  58. static auto calendars = []() {
  59. auto calendars = available_calendars("und"sv);
  60. quick_sort(calendars);
  61. return calendars;
  62. }();
  63. return calendars;
  64. }
  65. Vector<String> available_calendars(StringView locale)
  66. {
  67. UErrorCode status = U_ZERO_ERROR;
  68. auto locale_data = LocaleData::for_locale(locale);
  69. if (!locale_data.has_value())
  70. return {};
  71. auto keywords = adopt_own_if_nonnull(icu::Calendar::getKeywordValuesForLocale("calendar", locale_data->locale(), 0, status));
  72. if (icu_failure(status))
  73. return {};
  74. auto calendars = icu_string_enumeration_to_list(move(keywords));
  75. for (auto& calendar : calendars) {
  76. if (calendar == "gregorian"sv)
  77. calendar = "gregory"_string;
  78. else if (calendar == "ethiopic-amete-alem"sv)
  79. calendar = "ethioaa"_string;
  80. }
  81. return calendars;
  82. }
  83. Vector<String> const& available_currencies()
  84. {
  85. static auto currencies = []() -> Vector<String> {
  86. UErrorCode status = U_ZERO_ERROR;
  87. auto* currencies = ucurr_openISOCurrencies(UCURR_ALL, &status);
  88. ScopeGuard guard { [&]() { uenum_close(currencies); } };
  89. if (icu_failure(status))
  90. return {};
  91. Vector<String> result;
  92. while (true) {
  93. i32 length = 0;
  94. char const* next = uenum_next(currencies, &length, &status);
  95. if (icu_failure(status))
  96. return {};
  97. if (next == nullptr)
  98. break;
  99. // https://unicode-org.atlassian.net/browse/ICU-21687
  100. if (StringView currency { next, static_cast<size_t>(length) }; currency != "LSM"sv)
  101. result.append(MUST(String::from_utf8(currency)));
  102. }
  103. quick_sort(result);
  104. return result;
  105. }();
  106. return currencies;
  107. }
  108. Vector<String> const& available_collation_case_orderings()
  109. {
  110. static Vector<String> case_orderings { "false"_string, "lower"_string, "upper"_string };
  111. return case_orderings;
  112. }
  113. Vector<String> const& available_collation_numeric_orderings()
  114. {
  115. static Vector<String> case_orderings { "false"_string, "true"_string };
  116. return case_orderings;
  117. }
  118. Vector<String> const& available_collations()
  119. {
  120. // FIXME: Implement this when we fully support Intl.Collator.
  121. static Vector<String> collations { "default"_string };
  122. return collations;
  123. }
  124. Vector<String> available_collations(StringView)
  125. {
  126. // FIXME: Implement this when we fully support Intl.Collator.
  127. return available_collations();
  128. }
  129. Vector<String> const& available_hour_cycles()
  130. {
  131. static Vector<String> case_orderings { "h11"_string, "h12"_string, "h23"_string, "h24"_string };
  132. return case_orderings;
  133. }
  134. Vector<String> available_hour_cycles(StringView locale)
  135. {
  136. auto preferred_hour_cycle = default_hour_cycle(locale);
  137. if (!preferred_hour_cycle.has_value())
  138. return available_hour_cycles();
  139. Vector<String> hour_cycles;
  140. hour_cycles.append(MUST(String::from_utf8(hour_cycle_to_string(*preferred_hour_cycle))));
  141. for (auto const& hour_cycle : available_hour_cycles()) {
  142. if (hour_cycle != hour_cycles[0])
  143. hour_cycles.append(hour_cycle);
  144. }
  145. return hour_cycles;
  146. }
  147. Vector<String> const& available_number_systems()
  148. {
  149. static auto number_systems = []() -> Vector<String> {
  150. UErrorCode status = U_ZERO_ERROR;
  151. auto keywords = adopt_own_if_nonnull(icu::NumberingSystem::getAvailableNames(status));
  152. if (icu_failure(status))
  153. return {};
  154. auto number_systems = icu_string_enumeration_to_list(move(keywords), [&](char const* keyword) {
  155. auto system = adopt_own_if_nonnull(icu::NumberingSystem::createInstanceByName(keyword, status));
  156. if (icu_failure(status))
  157. return false;
  158. return !static_cast<bool>(system->isAlgorithmic());
  159. });
  160. quick_sort(number_systems);
  161. return number_systems;
  162. }();
  163. return number_systems;
  164. }
  165. Vector<String> available_number_systems(StringView locale)
  166. {
  167. auto locale_data = LocaleData::for_locale(locale);
  168. if (!locale_data.has_value())
  169. return {};
  170. Vector<String> number_systems;
  171. auto const* preferred_number_system = locale_data->numbering_system().getName();
  172. number_systems.append(MUST(String::from_utf8({ preferred_number_system, strlen(preferred_number_system) })));
  173. for (auto const& number_system : available_number_systems()) {
  174. if (number_system != number_systems[0])
  175. number_systems.append(number_system);
  176. }
  177. return number_systems;
  178. }
  179. }