diff options
| author | Rutger Broekhoff | 2026-09-10 10:59:59 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2026-09-10 10:59:59 +0200 |
| commit | a4408b5ac4ec10421bf9a7d5bf277946ec9f786e (patch) | |
| tree | 0a5214580a1557b3eb85f0a3ab0e15c5c9ec9f50 /server/src/locale.cpp | |
| parent | 4031e77edd2abd694114be698312d086bce6b425 (diff) | |
| download | routemon-main.tar.gz routemon-main.zip | |
Diffstat (limited to 'server/src/locale.cpp')
| -rw-r--r-- | server/src/locale.cpp | 121 |
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 | ||
| 72 | auto 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 | |||
| 83 | auto bcp47_tag::id = std::locale::id{}; | ||
| 84 | |||
| 85 | bcp47_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 | |||
| 95 | auto bcp47_tag::tag() const -> std::string_view { return tag_; } | ||
| 96 | |||
| 97 | auto 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. |
| 73 | auto selector::ltrim_ows(std::string_view s) -> std::string_view | 122 | auto 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 | ||
| 90 | auto 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 | |||
| 143 | auto selector::select(std::string_view accept_language) const -> std::locale | 139 | auto 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 | |||
| 203 | auto 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 |