HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
language_tag_intf.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/utility.hpp"
11#include "../algorithm/algorithm.hpp"
12#include "../macros.hpp"
13#include <vector>
14#include <string_view>
15#include <coroutine>
16#include <format>
17#include <string>
18
19hi_export_module(hikogui.i18n.language_tag : intf);
20
21hi_export namespace hi { inline namespace v1 {
22
30hi_export class language_tag {
31public:
32 iso_639 language;
33 iso_15924 script;
34 iso_3166 region;
35 uint16_t _reserved = 0;
36
37 constexpr language_tag() noexcept {}
38 constexpr language_tag(language_tag const&) noexcept = default;
39 constexpr language_tag(language_tag&&) noexcept = default;
40 constexpr language_tag& operator=(language_tag const&) noexcept = default;
41 constexpr language_tag& operator=(language_tag&&) noexcept = default;
42
43 constexpr language_tag(iso_639 const& language, iso_15924 const& script = {}, iso_3166 const& region = {}) noexcept :
44 language(language), script(script), region(region)
45 {
46 }
47
48 constexpr language_tag(iso_639 const& language, iso_3166 const& region) noexcept : language_tag(language, {}, region) {}
49
62 language_tag(std::string_view str);
63
68 [[nodiscard]] static language_tag parse(std::string_view str);
69
72 [[nodiscard]] bool empty() const noexcept
73 {
74 return language.empty() and script.empty() and region.empty();
75 }
76
79 explicit operator bool() const noexcept
80 {
81 return not empty();
82 }
83
91 [[nodiscard]] generator<language_tag> variants() const noexcept
92 {
93 co_yield *this;
94 if (script and region) {
95 co_yield language_tag{language, region};
96 co_yield language_tag{language, script};
97 }
98 if (script or region) {
99 co_yield language_tag{language};
100 }
101 }
102
111 [[nodiscard]] generator<language_tag> canonical_variants() const noexcept
112 {
113 auto const check = expand();
114 for (auto const& tag : variants()) {
115 if (tag.expand() == check) {
116 co_yield tag;
117 }
118 }
119 }
120
123 [[nodiscard]] std::vector<language_tag> all_variants() const noexcept
124 {
125 auto r = make_vector(variants());
126
127 // And languages variants from expanded variants.
128 for (auto const variant : variants()) {
129 auto expanded_variant = variant.expand();
130 for (auto const expanded_variant_variants : expanded_variant.variants()) {
131 if (std::find(r.begin(), r.end(), expanded_variant_variants) == r.end()) {
132 r.push_back(expanded_variant_variants);
133 }
134 }
135 }
136 return r;
137 }
138
143 [[nodiscard]] language_tag expand() const noexcept;
144
147 [[nodiscard]] language_tag shrink() const noexcept
148 {
149 auto last_variant = *this;
150 for (auto const& variant : canonical_variants()) {
151 last_variant = variant;
152 }
153 return last_variant;
154 }
155
160 [[nodiscard]] iso_15924 default_script() const noexcept
161 {
162 return expand().script;
163 }
164
169 [[nodiscard]] bool left_to_right() const noexcept
170 {
171 return default_script().left_to_right();
172 }
173
174 [[nodiscard]] constexpr friend std::string to_string(language_tag const &rhs) noexcept
175 {
176 auto r = std::string{};
177 r += rhs.language.code();
178 if (rhs.script) {
179 r += '-';
180 r += rhs.script.code4();
181 }
182 if (rhs.region) {
183 r += "-";
184 r += rhs.region.code2();
185 }
186 return r;
187 }
188
191 [[nodiscard]] constexpr friend bool matches(language_tag const& lhs, language_tag const& rhs) noexcept
192 {
193 if (lhs.language and rhs.language and lhs.language != rhs.language) {
194 return false;
195 }
196 if (lhs.script and rhs.script and lhs.script != rhs.script) {
197 return false;
198 }
199 if (lhs.region and rhs.region and lhs.region != rhs.region) {
200 return false;
201 }
202 return true;
203 }
204
205 [[nodiscard]] constexpr friend bool operator==(language_tag const&, language_tag const&) noexcept = default;
206};
207
216hi_export [[nodiscard]] std::vector<language_tag> variants(std::vector<language_tag> languages);
217
218}} // namespace hi::inline v1
219
220hi_export template<>
221struct std::hash<hi::language_tag> {
222 [[nodiscard]] size_t operator()(hi::language_tag const& rhs) const noexcept
223 {
224 return hi::hash_mix(
225 std::hash<hi::iso_639>{}(rhs.language),
226 std::hash<hi::iso_15924>{}(rhs.script),
227 std::hash<hi::iso_3166>{}(rhs.region));
228 }
229};
230
231// XXX #617 MSVC bug does not handle partial specialization in modules.
232hi_export template<>
233struct std::formatter<hi::language_tag, char> : std::formatter<std::string_view, char> {
234 auto format(hi::language_tag const& t, auto& fc) const
235 {
236 return std::formatter<std::string_view, char>::format(to_string(t), fc);
237 }
238};
239
240// XXX #618 C++23 should have this fixed?
241// XXX #617 MSVC bug does not handle partial specialization in modules.
242hi_export template<>
243struct std::formatter<std::vector<hi::language_tag>, char> : std::formatter<std::string_view, char> {
244 auto format(std::vector<hi::language_tag> const& t, auto& fc) const
245 {
246 auto r = std::string{};
247 for (auto const language : t) {
248 if (not r.empty()) {
249 r += ", ";
250 }
251 r += std::format("{}", language);
252 }
253 return std::formatter<std::string_view, char>::format(r, fc);
254 }
255};
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:20
std::vector< language_tag > variants(std::vector< language_tag > languages)
Add variants to the list of languages.
Definition language_tag_impl.hpp:2077
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
ISO-15924 script code.
Definition iso_15924_intf.hpp:23
constexpr bool left_to_right() const noexcept
Is this script written left-to-right.
Definition iso_15924_impl.hpp:349
The IETF BCP 47 language tag.
Definition language_tag_intf.hpp:30
language_tag expand() const noexcept
Expand the language tag to include script and language.
Definition language_tag_impl.hpp:2043
bool empty() const noexcept
Check if the language tag is empty.
Definition language_tag_intf.hpp:72
generator< language_tag > canonical_variants() const noexcept
Get variants of the language_tag.
Definition language_tag_intf.hpp:111
bool left_to_right() const noexcept
The language direction for this language-tag.
Definition language_tag_intf.hpp:169
iso_15924 default_script() const noexcept
Get the default-script for this language.
Definition language_tag_intf.hpp:160
generator< language_tag > variants() const noexcept
Get variants of the language_tag.
Definition language_tag_intf.hpp:91
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_intf.hpp:191
static language_tag parse(std::string_view str)
Parse the language, script and region raw from the string.
Definition language_tag_impl.hpp:1969
std::vector< language_tag > all_variants() const noexcept
Creates variants of a language tag, including those by expanding the normal variants.
Definition language_tag_intf.hpp:123
language_tag shrink() const noexcept
Get a tag with only the language.
Definition language_tag_intf.hpp:147
T find(T... args)
T operator()(T... args)
T to_string(T... args)