From a5d95afb96eb9a3b65d82f5f84bf8e4a4fb4c8ac Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Fri, 28 Aug 2026 21:12:55 +0200 Subject: clang-format C++ sources --- server/src/locale.cppm | 434 +++++++++++++++++++++++++++---------------------- 1 file changed, 242 insertions(+), 192 deletions(-) (limited to 'server/src/locale.cppm') diff --git a/server/src/locale.cppm b/server/src/locale.cppm index 65ae2ea..8fd867b 100644 --- a/server/src/locale.cppm +++ b/server/src/locale.cppm @@ -11,235 +11,285 @@ import :util; export namespace blocale = boost::locale; export namespace routemon { - using lformat = blocale::format; - using blocale::translate; - using blocale::gettext; +using lformat = blocale::format; +using blocale::gettext; +using blocale::translate; } // namespace routemon namespace routemon::locale { - struct locale_priority { - float weight; - std::size_t original_index; - }; +struct locale_priority +{ + float weight; + std::size_t original_index; +}; - auto operator<(locale_priority const& lhs, locale_priority const& rhs) -> bool { - if (lhs.weight != rhs.weight) - return lhs.weight > rhs.weight; - return lhs.original_index < rhs.original_index; - } +auto operator<(locale_priority const& lhs, locale_priority const& rhs) -> bool +{ + if (lhs.weight != rhs.weight) + return lhs.weight > rhs.weight; + return lhs.original_index < rhs.original_index; +} - struct icu_locale_hash { - std::size_t operator()(icu::Locale const& l) const noexcept { - static_assert(sizeof(std::int32_t) < sizeof(std::size_t)); - std::int32_t hash = l.hashCode(); - if (hash < 0) { - return static_cast(std::numeric_limits::max()) + static_cast(-hash) + 1; - } else { - return static_cast(hash); - } +struct icu_locale_hash +{ + std::size_t operator()(icu::Locale const& l) const noexcept + { + static_assert(sizeof(std::int32_t) < sizeof(std::size_t)); + std::int32_t hash = l.hashCode(); + if (hash < 0) + { + return static_cast(std::numeric_limits::max()) + + static_cast(-hash) + 1; + } + else + { + return static_cast(hash); } - }; + } +}; + +using icu_locale_priority_map = + std::unordered_map; +using icu_priority_locale = std::pair; + +auto operator<(icu_priority_locale const& lhs, icu_priority_locale const& rhs) + -> bool +{ + return lhs.second < rhs.second; +} - using icu_locale_priority_map = std::unordered_map; - using icu_priority_locale = std::pair; +class icu_priority_locale_vec_iterator : public icu::Locale::Iterator +{ + std::size_t i_ = 0uz; + std::vector ls_; - auto operator<(icu_priority_locale const& lhs, icu_priority_locale const& rhs) -> bool { - return lhs.second < rhs.second; +public: + explicit icu_priority_locale_vec_iterator( + std::vector&& ls) + : ls_(std::move(ls)) + { } - class icu_priority_locale_vec_iterator : public icu::Locale::Iterator { - std::size_t i_ = 0uz; - std::vector ls_; + auto hasNext() const -> UBool override { return i_ < ls_.size(); } - public: - explicit icu_priority_locale_vec_iterator(std::vector&& ls) - : ls_(std::move(ls)) - {} + auto next() -> icu::Locale const& override { return ls_[i_++].first; } - auto hasNext() const -> UBool override { - return i_ < ls_.size(); - } + ~icu_priority_locale_vec_iterator() override = default; +}; - auto next() -> icu::Locale const& override { - return ls_[i_++].first; - } +export template +concept locale_input_range = + std::ranges::input_range + && std::same_as< + std::locale const&, std::ranges::range_const_reference_t>; - ~icu_priority_locale_vec_iterator() override = default; - }; - - export template - concept locale_input_range = - std::ranges::input_range && - std::same_as>; - - // Helps select a locale based on the Accept-Language header in an - // HTTP request. - export class selector { - std::locale default_; - icu::LocaleMatcher matcher_; - std::shared_ptr lgen_; - - auto make_matcher(locale_input_range auto supported_locales, std::locale default_locale) { - auto builder = icu::LocaleMatcher::Builder{}; - for (auto const& supported_locale : supported_locales) { - auto const& supported_locale_info = std::use_facet(supported_locale); - auto supported_icu_locale = icu::Locale{supported_locale_info.name().c_str()}; - if (supported_icu_locale.isBogus()) - throw std::runtime_error{"supported locale gives rise to bogus ICU locale"}; - builder.addSupportedLocale(supported_icu_locale); - } - auto const& default_locale_info = std::use_facet(default_locale); - auto default_icu_locale = icu::Locale{default_locale_info.name().c_str()}; - if (default_icu_locale.isBogus()) - throw std::runtime_error{"default locale gives rise to bogus ICU locale"}; - builder.setDefaultLocale(&default_icu_locale); - auto ec = UErrorCode::U_ZERO_ERROR; - auto matcher = builder.build(ec); - if (U_FAILURE(ec)) - throw std::runtime_error{"failed to build icu::LocaleMatcher"}; - return matcher; +// Helps select a locale based on the Accept-Language header in an +// HTTP request. +export class selector +{ + std::locale default_; + icu::LocaleMatcher matcher_; + std::shared_ptr lgen_; + + auto make_matcher( + locale_input_range auto supported_locales, std::locale default_locale) + { + auto builder = icu::LocaleMatcher::Builder{}; + for (auto const& supported_locale : supported_locales) + { + auto const& supported_locale_info = + std::use_facet(supported_locale); + auto supported_icu_locale = + icu::Locale{supported_locale_info.name().c_str()}; + if (supported_icu_locale.isBogus()) + throw std::runtime_error{ + "supported locale gives rise to bogus ICU locale" + }; + builder.addSupportedLocale(supported_icu_locale); } + auto const& default_locale_info = + std::use_facet(default_locale); + auto default_icu_locale = icu::Locale{default_locale_info.name().c_str()}; + if (default_icu_locale.isBogus()) + throw std::runtime_error{"default locale gives rise to bogus ICU locale"}; + builder.setDefaultLocale(&default_icu_locale); + auto ec = UErrorCode::U_ZERO_ERROR; + auto matcher = builder.build(ec); + if (U_FAILURE(ec)) + throw std::runtime_error{"failed to build icu::LocaleMatcher"}; + return matcher; + } - // Trimming optional whitespace as defined in RFC 9110, § 12.4.2. - static auto ltrim_ows(std::string_view s) -> std::string_view { - if (auto i = s.find_first_not_of(" \t"); i != std::string_view::npos) - s.remove_prefix(i); - return s; + // Trimming optional whitespace as defined in RFC 9110, § 12.4.2. + static auto ltrim_ows(std::string_view s) -> std::string_view + { + if (auto i = s.find_first_not_of(" \t"); i != std::string_view::npos) + s.remove_prefix(i); + return s; + } + static auto rtrim_ows(std::string_view s) -> std::string_view + { + if (auto i = s.find_last_not_of(" \t"); i != std::string_view::npos) + return s.substr(0, i + 1); + return s; + } + static auto trim_ows(std::string_view s) -> std::string_view + { + return rtrim_ows(ltrim_ows(s)); + } + + auto from_icu_locale(icu::Locale const& l) const -> std::locale + { + auto posix_name = std::string{l.getLanguage()}; + if (l.getScript() && std::strlen(l.getScript()) > 0) + { + posix_name += "_"; + posix_name += l.getScript(); } - static auto rtrim_ows(std::string_view s) -> std::string_view { - if (auto i = s.find_last_not_of(" \t"); i != std::string_view::npos) - return s.substr(0, i + 1); - return s; + if (l.getCountry() && std::strlen(l.getCountry()) > 0) + { + posix_name += "_"; + posix_name += l.getCountry(); } - static auto trim_ows(std::string_view s) -> std::string_view { - return rtrim_ows(ltrim_ows(s)); + posix_name += ".UTF-8"; + auto added_at = false; + if (l.getVariant() && std::strlen(l.getVariant()) > 0) + { + added_at = true; + posix_name += "@"; + posix_name += l.getVariant(); } - - auto from_icu_locale(icu::Locale const& l) const -> std::locale { - auto posix_name = std::string{l.getLanguage()}; - if (l.getScript() && std::strlen(l.getScript()) > 0) { - posix_name += "_"; - posix_name += l.getScript(); - } - if (l.getCountry() && std::strlen(l.getCountry()) > 0) { - posix_name += "_"; - posix_name += l.getCountry(); - } - posix_name += ".UTF-8"; - auto added_at = false; - if (l.getVariant() && std::strlen(l.getVariant()) > 0) { - added_at = true; - posix_name += "@"; - posix_name += l.getVariant(); - } - auto ec = UErrorCode::U_ZERO_ERROR; - auto* keywords = l.createKeywords(ec); - if (U_FAILURE(ec)) - throw std::runtime_error{"failed to create keywords"}; - if (keywords) { - std::int32_t kw_len = 0; - char const* kw = nullptr; - while (kw = keywords->next(&kw_len, ec), !U_FAILURE(ec) && kw) { - auto value = l.getKeywordValue(icu::StringPiece(kw, kw_len), ec); - if (!added_at) { - posix_name += "@"; - added_at = true; - } else { - posix_name += ";"; - } - posix_name += kw; - posix_name += "="; - posix_name += value; + auto ec = UErrorCode::U_ZERO_ERROR; + auto* keywords = l.createKeywords(ec); + if (U_FAILURE(ec)) + throw std::runtime_error{"failed to create keywords"}; + if (keywords) + { + std::int32_t kw_len = 0; + char const* kw = nullptr; + while (kw = keywords->next(&kw_len, ec), !U_FAILURE(ec) && kw) + { + auto value = + l.getKeywordValue(icu::StringPiece(kw, kw_len), ec); + if (!added_at) + { + posix_name += "@"; + added_at = true; + } + else + { + posix_name += ";"; } - if (U_FAILURE(ec)) - throw std::runtime_error{"failed to iterate over keywords"}; - delete keywords; + posix_name += kw; + posix_name += "="; + posix_name += value; } - return lgen_->generate(posix_name); + if (U_FAILURE(ec)) + throw std::runtime_error{"failed to iterate over keywords"}; + delete keywords; } + return lgen_->generate(posix_name); + } + +public: + // Note: lgen must live at least as long as the selector constructed here! + // It is unfortunately not possible to copy/move a blocale::generator. + explicit selector( + locale_input_range auto locales, std::locale default_, + std::shared_ptr lgen) + : default_{default_}, matcher_{make_matcher(locales, default_)}, lgen_{lgen} + { + } + + auto select(std::string_view accept_language) const -> std::locale + { + using namespace std::literals::string_view_literals; + // NOTE: can also contain a *;q=0.1 + // q should have at most 3 digits after period + auto dlpm = icu_locale_priority_map{}; + for (auto const [i, lang_prio] : + accept_language | std::views::split(","sv) | std::views::enumerate) + { + auto [lang_range_ut, mweight_ut] = + util::split_on(std::string_view{lang_prio}, ';'); + auto lang_range_str = trim_ows(lang_range_ut); + auto mweight_str = mweight_ut.transform(trim_ows); + if (lang_range_str == "*") + break; + + auto ec = UErrorCode::U_ZERO_ERROR; + auto icu_locale = icu::Locale::forLanguageTag(lang_range_str, ec); + if (U_FAILURE(ec) || icu_locale.isBogus()) + continue; // ignore this locale - public: - // Note: lgen must live at least as long as the selector constructed here! - // It is unfortunately not possible to copy/move a blocale::generator. - explicit selector(locale_input_range auto locales, std::locale default_, std::shared_ptr lgen) - : default_{default_}, matcher_{make_matcher(locales, default_)}, lgen_{lgen} - {} - - auto select(std::string_view accept_language) const -> std::locale { - using namespace std::literals::string_view_literals; - // NOTE: can also contain a *;q=0.1 - // q should have at most 3 digits after period - auto dlpm = icu_locale_priority_map{}; - for (auto const [i, lang_prio] : accept_language | std::views::split(","sv) | std::views::enumerate) { - auto [lang_range_ut, mweight_ut] = util::split_on(std::string_view{lang_prio}, ';'); - auto lang_range_str = trim_ows(lang_range_ut); - auto mweight_str = mweight_ut.transform(trim_ows); - if (lang_range_str == "*") - break; - - auto ec = UErrorCode::U_ZERO_ERROR; - auto icu_locale = icu::Locale::forLanguageTag(lang_range_str, ec); - if (U_FAILURE(ec) || icu_locale.isBogus()) - continue; // ignore this locale - - auto weight = 1.0f; - if (mweight_str && mweight_str->starts_with("q=")) { - auto weight_str = mweight_str->substr(2, 4); - if (auto mweight = util::parse_float(weight_str, std::chars_format::fixed); - mweight && 0.0f < *mweight && *mweight < 1.0f) { - weight = *mweight; - } + auto weight = 1.0f; + if (mweight_str && mweight_str->starts_with("q=")) + { + auto weight_str = mweight_str->substr(2, 4); + if (auto mweight = + util::parse_float(weight_str, std::chars_format::fixed); + mweight && 0.0f < *mweight && *mweight < 1.0f) + { + weight = *mweight; } + } - if (weight > 0.0f) { - dlpm[icu_locale] = { + if (weight > 0.0f) + { + dlpm[icu_locale] = { .weight = weight, .original_index = static_cast(i), - }; - } else { - dlpm.erase(icu_locale); - } + }; + } + else + { + dlpm.erase(icu_locale); } - - auto desired_locales = std::vector{dlpm.begin(), dlpm.end()}; - std::sort(desired_locales.begin(), desired_locales.end()); - auto it = icu_priority_locale_vec_iterator{std::move(desired_locales)}; - auto ec = UErrorCode::U_ZERO_ERROR; - auto res = matcher_.getBestMatchResult(it, ec); - if (U_FAILURE(ec)) - return default_; - auto resolved = res.makeResolvedLocale(ec); // TODO: maybe don't? - if (U_FAILURE(ec)) - return from_icu_locale(*res.getSupportedLocale()); - return from_icu_locale(resolved); } - }; - export auto to_bcp47_lang_tag(std::locale locale) -> std::optional { - auto const& locale_info = std::use_facet(locale); + auto desired_locales = + std::vector{dlpm.begin(), dlpm.end()}; + std::sort(desired_locales.begin(), desired_locales.end()); + auto it = icu_priority_locale_vec_iterator{std::move(desired_locales)}; auto ec = UErrorCode::U_ZERO_ERROR; - auto bcp47_lang_tag = icu::Locale{locale_info.name().c_str()}.toLanguageTag(ec); + auto res = matcher_.getBestMatchResult(it, ec); + if (U_FAILURE(ec)) + return default_; + auto resolved = res.makeResolvedLocale(ec); // TODO: maybe don't? if (U_FAILURE(ec)) - return std::nullopt; - return bcp47_lang_tag; + return from_icu_locale(*res.getSupportedLocale()); + return from_icu_locale(resolved); } +}; + +export auto to_bcp47_lang_tag(std::locale locale) -> std::optional +{ + auto const& locale_info = std::use_facet(locale); + auto ec = UErrorCode::U_ZERO_ERROR; + auto bcp47_lang_tag = + icu::Locale{locale_info.name().c_str()}.toLanguageTag(ec); + if (U_FAILURE(ec)) + return std::nullopt; + return bcp47_lang_tag; +} #ifdef LOCALEDIR -# define LOCALEDIR_AUX_XSTR(s) LOCALEDIR_AUX_STR(s) -# define LOCALEDIR_AUX_STR(s) #s - constexpr auto messages_path = std::string_view{LOCALEDIR_AUX_XSTR(LOCALEDIR)}; -# undef LOCALEDIR_AUX_STR -# undef LOCALEDIR_AUX_XSTR -#else // ifdef LOCALEDIR - constexpr auto messages_path = std::string_view{"locale/dev"}; +#define LOCALEDIR_AUX_XSTR(s) LOCALEDIR_AUX_STR(s) +#define LOCALEDIR_AUX_STR(s) #s +constexpr auto messages_path = std::string_view{LOCALEDIR_AUX_XSTR(LOCALEDIR)}; +#undef LOCALEDIR_AUX_STR +#undef LOCALEDIR_AUX_XSTR +#else // ifdef LOCALEDIR +constexpr auto messages_path = std::string_view{"locale/dev"}; #endif // ifdef LOCALEDIR - export auto make_generator() -> std::shared_ptr { - auto lgen = std::make_shared(); - lgen->add_messages_path(std::string{messages_path}); - lgen->add_messages_domain("routemon"); - return std::static_pointer_cast(lgen); - } +export auto make_generator() -> std::shared_ptr +{ + auto lgen = std::make_shared(); + lgen->add_messages_path(std::string{messages_path}); + lgen->add_messages_domain("routemon"); + return std::static_pointer_cast(lgen); +} } // namespace routemon::locale -- cgit v1.3