From a4408b5ac4ec10421bf9a7d5bf277946ec9f786e Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Thu, 10 Sep 2026 10:59:59 +0200 Subject: Timing and slow locale negotation fixes --- server/src/locale.cppm | 65 ++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 39 deletions(-) (limited to 'server/src/locale.cppm') diff --git a/server/src/locale.cppm b/server/src/locale.cppm index 263de1d..00a3d36 100644 --- a/server/src/locale.cppm +++ b/server/src/locale.cppm @@ -26,65 +26,52 @@ concept locale_input_range = && std:: same_as>; +class bcp47_tag : public std::locale::facet +{ + std::string tag_; + +public: + static std::locale::id id; + + explicit bcp47_tag(std::locale locale); + ~bcp47_tag() override = default; + + auto tag() const -> std::string_view; +}; + // Helps select a locale based on the Accept-Language header in an // HTTP request. export class selector { + std::vector supported_; 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; - } + static auto make_matcher(std::vector const& supported_locales) + -> icu::LocaleMatcher; // Trimming optional whitespace as defined in RFC 9110, ยง 12.4.2. static auto ltrim_ows(std::string_view s) -> std::string_view; static auto rtrim_ows(std::string_view s) -> std::string_view; static auto trim_ows(std::string_view s) -> std::string_view; - auto from_icu_locale(icu::Locale const& l) const -> std::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} + explicit selector(locale_input_range auto supported, std::locale default_) + : supported_{ + std::from_range, + supported + | std::views::transform( + [](std::locale const& locale) -> std::locale + { return std::locale{locale, new bcp47_tag{locale}}; }) + }, + default_{std::locale{default_, new bcp47_tag{default_}}}, + matcher_{make_matcher(supported_)} { } auto select(std::string_view accept_language) const -> std::locale; }; -auto to_bcp47_lang_tag(std::locale locale) -> std::optional; - export auto make_generator() -> std::shared_ptr; } // namespace routemon::locale -- cgit v1.3