summaryrefslogtreecommitdiffstats
path: root/server/src/locale.cpp
diff options
context:
space:
mode:
authorRutger Broekhoff2026-09-10 10:59:59 +0200
committerRutger Broekhoff2026-09-10 10:59:59 +0200
commita4408b5ac4ec10421bf9a7d5bf277946ec9f786e (patch)
tree0a5214580a1557b3eb85f0a3ab0e15c5c9ec9f50 /server/src/locale.cpp
parent4031e77edd2abd694114be698312d086bce6b425 (diff)
downloadroutemon-main.tar.gz
routemon-main.zip
Timing and slow locale negotation fixesHEADmain
Diffstat (limited to 'server/src/locale.cpp')
-rw-r--r--server/src/locale.cpp121
1 files changed, 53 insertions, 68 deletions
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:
69 ~icu_priority_locale_vec_iterator() override = default; 69 ~icu_priority_locale_vec_iterator() override = default;
70}; 70};
71 71
72auto to_bcp47_lang_tag(std::locale locale) -> std::optional<std::string>
73{
74 auto const& locale_info = std::use_facet<blocale::info>(locale);
75 auto ec = UErrorCode::U_ZERO_ERROR;
76 auto bcp47_lang_tag =
77 icu::Locale{locale_info.name().c_str()}.toLanguageTag<std::string>(ec);
78 if (U_FAILURE(ec))
79 return std::nullopt;
80 return bcp47_lang_tag;
81}
82
83auto bcp47_tag::id = std::locale::id{};
84
85bcp47_tag::bcp47_tag(std::locale locale)
86{
87 auto mtag = to_bcp47_lang_tag(locale);
88 if (!mtag)
89 throw std::invalid_argument{
90 "locale name cannot be expressed as a BCP 47 language tag"
91 };
92 tag_ = *mtag;
93}
94
95auto bcp47_tag::tag() const -> std::string_view { return tag_; }
96
97auto selector::make_matcher(std::vector<std::locale> const& supported_locales)
98 -> icu::LocaleMatcher
99{
100 auto builder = icu::LocaleMatcher::Builder{};
101 for (auto const& supported_locale : supported_locales)
102 {
103 auto const& supported_locale_info =
104 std::use_facet<blocale::info>(supported_locale);
105 auto supported_icu_locale =
106 icu::Locale{supported_locale_info.name().c_str()};
107 if (supported_icu_locale.isBogus())
108 throw std::runtime_error{
109 "supported locale gives rise to bogus ICU locale"
110 };
111 builder.addSupportedLocale(supported_icu_locale);
112 }
113 builder.setNoDefaultLocale();
114 auto ec = UErrorCode::U_ZERO_ERROR;
115 auto matcher = builder.build(ec);
116 if (U_FAILURE(ec))
117 throw std::runtime_error{"failed to build icu::LocaleMatcher"};
118 return matcher;
119}
120
72// Trimming optional whitespace as defined in RFC 9110, § 12.4.2. 121// Trimming optional whitespace as defined in RFC 9110, § 12.4.2.
73auto selector::ltrim_ows(std::string_view s) -> std::string_view 122auto selector::ltrim_ows(std::string_view s) -> std::string_view
74{ 123{
@@ -87,59 +136,6 @@ auto selector::trim_ows(std::string_view s) -> std::string_view
87 return rtrim_ows(ltrim_ows(s)); 136 return rtrim_ows(ltrim_ows(s));
88} 137}
89 138
90auto selector::from_icu_locale(icu::Locale const& l) const -> std::locale
91{
92 auto posix_name = std::string{l.getLanguage()};
93 if (l.getScript() && std::strlen(l.getScript()) > 0)
94 {
95 posix_name += "_";
96 posix_name += l.getScript();
97 }
98 if (l.getCountry() && std::strlen(l.getCountry()) > 0)
99 {
100 posix_name += "_";
101 posix_name += l.getCountry();
102 }
103 posix_name += ".UTF-8";
104 auto added_at = false;
105 if (l.getVariant() && std::strlen(l.getVariant()) > 0)
106 {
107 added_at = true;
108 posix_name += "@";
109 posix_name += l.getVariant();
110 }
111 auto ec = UErrorCode::U_ZERO_ERROR;
112 auto* keywords = l.createKeywords(ec);
113 if (U_FAILURE(ec))
114 throw std::runtime_error{"failed to create keywords"};
115 if (keywords)
116 {
117 std::int32_t kw_len = 0;
118 char const* kw = nullptr;
119 while (kw = keywords->next(&kw_len, ec), !U_FAILURE(ec) && kw)
120 {
121 auto value =
122 l.getKeywordValue<std::string>(icu::StringPiece(kw, kw_len), ec);
123 if (!added_at)
124 {
125 posix_name += "@";
126 added_at = true;
127 }
128 else
129 {
130 posix_name += ";";
131 }
132 posix_name += kw;
133 posix_name += "=";
134 posix_name += value;
135 }
136 if (U_FAILURE(ec))
137 throw std::runtime_error{"failed to iterate over keywords"};
138 delete keywords;
139 }
140 return lgen_->generate(posix_name);
141}
142
143auto selector::select(std::string_view accept_language) const -> std::locale 139auto selector::select(std::string_view accept_language) const -> std::locale
144{ 140{
145 using namespace std::literals::string_view_literals; 141 using namespace std::literals::string_view_literals;
@@ -194,21 +190,10 @@ auto selector::select(std::string_view accept_language) const -> std::locale
194 auto res = matcher_.getBestMatchResult(it, ec); 190 auto res = matcher_.getBestMatchResult(it, ec);
195 if (U_FAILURE(ec)) 191 if (U_FAILURE(ec))
196 return default_; 192 return default_;
197 auto resolved = res.makeResolvedLocale(ec); // TODO: maybe don't? 193 auto i = res.getSupportedIndex();
198 if (U_FAILURE(ec)) 194 if (i == -1)
199 return from_icu_locale(*res.getSupportedLocale()); 195 return default_;
200 return from_icu_locale(resolved); 196 return supported_[util::size_from_int(i).value()];
201}
202
203auto to_bcp47_lang_tag(std::locale locale) -> std::optional<std::string>
204{
205 auto const& locale_info = std::use_facet<blocale::info>(locale);
206 auto ec = UErrorCode::U_ZERO_ERROR;
207 auto bcp47_lang_tag =
208 icu::Locale{locale_info.name().c_str()}.toLanguageTag<std::string>(ec);
209 if (U_FAILURE(ec))
210 return std::nullopt;
211 return bcp47_lang_tag;
212} 197}
213 198
214#ifdef LOCALEDIR 199#ifdef LOCALEDIR