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.cpp | 121 ++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 68 deletions(-) (limited to 'server/src/locale.cpp') diff --git a/server/src/locale.cpp b/server/src/locale.cpp index 652349c..92bc33b 100644 --- a/server/src/locale.cpp +++ b/server/src/locale.cpp @@ -69,6 +69,55 @@ public: ~icu_priority_locale_vec_iterator() override = default; }; +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; +} + +auto bcp47_tag::id = std::locale::id{}; + +bcp47_tag::bcp47_tag(std::locale locale) +{ + auto mtag = to_bcp47_lang_tag(locale); + if (!mtag) + throw std::invalid_argument{ + "locale name cannot be expressed as a BCP 47 language tag" + }; + tag_ = *mtag; +} + +auto bcp47_tag::tag() const -> std::string_view { return tag_; } + +auto selector::make_matcher(std::vector const& supported_locales) + -> icu::LocaleMatcher +{ + 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); + } + builder.setNoDefaultLocale(); + 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. auto selector::ltrim_ows(std::string_view s) -> std::string_view { @@ -87,59 +136,6 @@ auto selector::trim_ows(std::string_view s) -> std::string_view return rtrim_ows(ltrim_ows(s)); } -auto selector::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; - } - if (U_FAILURE(ec)) - throw std::runtime_error{"failed to iterate over keywords"}; - delete keywords; - } - return lgen_->generate(posix_name); -} - auto selector::select(std::string_view accept_language) const -> std::locale { using namespace std::literals::string_view_literals; @@ -194,21 +190,10 @@ auto selector::select(std::string_view accept_language) const -> std::locale 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); -} - -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; + auto i = res.getSupportedIndex(); + if (i == -1) + return default_; + return supported_[util::size_from_int(i).value()]; } #ifdef LOCALEDIR -- cgit v1.3