HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
language_tag.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "iso_15924.hpp"
8#include "iso_3166.hpp"
9#include "iso_639.hpp"
10#include "../utility/module.hpp"
11#include "../generator.hpp"
12#include "../ranges.hpp"
13#include <vector>
14#include <string_view>
15
16namespace hi::inline v1 {
17
26public:
27 iso_639 language;
28 iso_15924 script;
29 iso_3166 region;
30 uint16_t _reserved = 0;
31
32 constexpr language_tag() noexcept {}
33 constexpr language_tag(language_tag const&) noexcept = default;
34 constexpr language_tag(language_tag&&) noexcept = default;
35 constexpr language_tag& operator=(language_tag const&) noexcept = default;
36 constexpr language_tag& operator=(language_tag&&) noexcept = default;
37
38 constexpr language_tag(iso_639 const& language, iso_15924 const& script = {}, iso_3166 const& region = {}) noexcept :
39 language(language), script(script), region(region)
40 {
41 }
42
43 constexpr language_tag(iso_639 const& language, iso_3166 const& region) noexcept : language_tag(language, {}, region) {}
44
57 language_tag(std::string_view str);
58
63 [[nodiscard]] static language_tag parse(std::string_view str);
64
67 [[nodiscard]] bool empty() const noexcept
68 {
69 return language.empty() and script.empty() and region.empty();
70 }
71
74 explicit operator bool() const noexcept
75 {
76 return not empty();
77 }
78
86 [[nodiscard]] generator<language_tag> variants() const noexcept
87 {
88 co_yield *this;
89 if (script and region) {
90 co_yield language_tag{language, region};
91 co_yield language_tag{language, script};
92 }
93 if (script or region) {
94 co_yield language_tag{language};
95 }
96 }
97
106 [[nodiscard]] generator<language_tag> canonical_variants() const noexcept
107 {
108 hilet check = expand();
109 for (hilet& tag : variants()) {
110 if (tag.expand() == check) {
111 co_yield tag;
112 }
113 }
114 }
115
118 [[nodiscard]] std::vector<language_tag> all_variants() const noexcept
119 {
120 auto r = make_vector(variants());
121
122 // And languages variants from expanded variants.
123 for (hilet variant : variants()) {
124 for (hilet expanded_variant : variant.expand().variants()) {
125 if (std::find(r.begin(), r.end(), expanded_variant) == r.end()) {
126 r.push_back(expanded_variant);
127 }
128 }
129 }
130 return r;
131 }
132
137 [[nodiscard]] language_tag expand() const noexcept;
138
141 [[nodiscard]] language_tag shrink() const noexcept
142 {
143 auto last_variant = *this;
144 for (hilet& variant : canonical_variants()) {
145 last_variant = variant;
146 }
147 return last_variant;
148 }
149
154 [[nodiscard]] iso_15924 default_script() const noexcept
155 {
156 return expand().script;
157 }
158
163 [[nodiscard]] bool left_to_right() const noexcept
164 {
165 return default_script().left_to_right();
166 }
167
168 [[nodiscard]] std::string to_string() const noexcept
169 {
170 auto r = std::string{};
171 r += language.code();
172 if (script) {
173 r += '-';
174 r += script.code4();
175 }
176 if (region) {
177 r += "-";
178 r += region.code2();
179 }
180 return r;
181 }
182
185 [[nodiscard]] constexpr friend bool matches(language_tag const& lhs, language_tag const& rhs) noexcept
186 {
187 if (lhs.language and rhs.language and lhs.language != rhs.language) {
188 return false;
189 }
190 if (lhs.script and rhs.script and lhs.script != rhs.script) {
191 return false;
192 }
193 if (lhs.region and rhs.region and lhs.region != rhs.region) {
194 return false;
195 }
196 return true;
197 }
198
199 [[nodiscard]] constexpr friend bool operator==(language_tag const&, language_tag const&) noexcept = default;
200};
201
211
212} // namespace hi::inline v1
213
214template<>
215struct std::hash<hi::language_tag> {
216 [[nodiscard]] size_t operator()(hi::language_tag const& rhs) const noexcept
217 {
218 return hi::hash_mix(
219 std::hash<hi::iso_639>{}(rhs.language),
220 std::hash<hi::iso_15924>{}(rhs.script),
221 std::hash<hi::iso_3166>{}(rhs.region));
222 }
223};
224
225template<typename CharT>
226struct std::formatter<hi::language_tag, CharT> : std::formatter<std::string_view, CharT> {
227 auto format(hi::language_tag const& t, auto& fc)
228 {
229 return std::formatter<std::string_view, CharT>::format(t.to_string(), fc);
230 }
231};
232
233// XXX C++23 should have this fixed?
234template<typename CharT>
235struct std::formatter<std::vector<hi::language_tag>, CharT> : std::formatter<std::string_view, CharT> {
236 auto format(std::vector<hi::language_tag> const& t, auto& fc)
237 {
238 auto r = std::string{};
239 for (hilet language : t) {
240 if (not r.empty()) {
241 r += ", ";
242 }
243 r += std::format("{}", language);
244 }
245 return std::formatter<std::string_view, CharT>::format(r, fc);
246 }
247};
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr std::vector< Value > make_vector(Range &&range)
Make a vector from a view.
Definition ranges.hpp:43
std::vector< language_tag > variants(std::vector< language_tag > languages)
Add variants to the list of languages.
geometry/margins.hpp
Definition cache.hpp:11
A return value for a generator-function.
Definition generator.hpp:29
ISO-15924 script code.
Definition iso_15924.hpp:17
std::string_view code4() const noexcept
Get the iso-15924 4-letter code.
ISO-3166 country code.
Definition iso_3166.hpp:15
ISO-639 language code.
Definition iso_639.hpp:23
constexpr std::string code() const noexcept
Get the 2 or 3 letter ISO-639 code.
Definition iso_639.hpp:150
constexpr bool empty() const noexcept
Check if the language is empty.
Definition iso_639.hpp:129
The IETF BCP 47 language tag.
Definition language_tag.hpp:25
bool empty() const noexcept
Check if the language tag is empty.
Definition language_tag.hpp:67
language_tag(std::string_view str)
Parse a language tag.
language_tag expand() const noexcept
Expand the language tag to include script and language.
generator< language_tag > canonical_variants() const noexcept
Get variants of the language_tag.
Definition language_tag.hpp:106
constexpr friend bool matches(language_tag const &lhs, language_tag const &rhs) noexcept
Check if two language_tags match for their non-empty fields.
Definition language_tag.hpp:185
generator< language_tag > variants() const noexcept
Get variants of the language_tag.
Definition language_tag.hpp:86
std::vector< language_tag > all_variants() const noexcept
Creates variants of a language tag, including those by expanding the normal variants.
Definition language_tag.hpp:118
static language_tag parse(std::string_view str)
Parse the language, script and region raw from the string.
bool left_to_right() const noexcept
The language direction for this language-tag.
Definition language_tag.hpp:163
iso_15924 default_script() const noexcept
Get the default-script for this language.
Definition language_tag.hpp:154
T find(T... args)
T operator()(T... args)