LeviLamina
Loading...
Searching...
No Matches
I18n.h
1#pragma once
2
3#include "ll/api/Expected.h"
4#include "ll/api/base/Concepts.h"
5#include "ll/api/base/FixedString.h"
6
7#if LL_HAS_CXX23
8#include <expected>
9#endif
10
11#include "fmt/args.h" // IWYU pragma: keep
12#include "fmt/chrono.h" // IWYU pragma: keep
13#include "fmt/color.h" // IWYU pragma: keep
14#include "fmt/compile.h" // IWYU pragma: keep
15#include "fmt/format.h" // IWYU pragma: keep
16#include "fmt/os.h" // IWYU pragma: keep
17#include "fmt/ranges.h" // IWYU pragma: keep
18#include "fmt/std.h" // IWYU pragma: keep
19
20// #define LL_I18N_COLLECT_STRINGS
21
22namespace ll::i18n {
23
24LLNDAPI std::string_view getDefaultLocaleCode();
25
26class I18n {
27 struct Impl;
28 std::unique_ptr<Impl> impl;
29
30public:
31 LLNDAPI I18n();
32 LLAPI ~I18n();
33
34 LLNDAPI Expected<> load(std::filesystem::path const& path) noexcept;
35
36 LLAPI void clear();
37
38 LLAPI void set(std::string_view localeCode, std::string_view key, std::string_view value);
39
40 LLNDAPI std::string_view get(std::string_view key, std::string_view localeCode) const;
41};
42
43inline I18n& getInstance() {
44 static I18n ins{};
45 return ins;
46}
47
48class I18nStringError : public ErrorInfoBase {
49private:
50 gsl::not_null<I18n*> mI18n;
51 std::string mKey;
52 fmt::dynamic_format_arg_store<fmt::format_context> mArgs;
53
54public:
55 template <typename... Args>
56 constexpr explicit I18nStringError(fmt::format_string<Args...> fmt, Args&&... args)
57 : mI18n(std::addressof(ll::i18n::getInstance())),
58 mKey(std::string{fmt.get().begin(), fmt.get().end()}) {
59 if constexpr (sizeof...(Args) > 0) {
60 // clang-format off
61 mArgs.reserve(sizeof...(Args), fmt::detail::count_named_args<Args...>());
62 ([&] {
63 if constexpr (fmt::detail::is_named_arg<std::decay_t<Args>>()) {
64 // from static_named_arg to named_arg
65 mArgs.push_back(fmt::arg(std::forward<Args>(args).name, std::forward<Args>(args).value));
66 } else {
67 mArgs.push_back(std::forward<Args>(args));
68 }
69 }(), ...);
70 // clang-format on
71 }
72 }
73
74public:
75 std::string message() const noexcept override { return message(ll::i18n::getDefaultLocaleCode()); }
76 std::string message(std::string_view localeCode) const noexcept override try {
77 auto pattern = mI18n->get(mKey, localeCode.empty() ? ll::i18n::getDefaultLocaleCode() : localeCode);
78 if (!pattern.empty()) try {
79 return fmt::vformat(pattern, mArgs);
80 } catch (...) {}
81 return fmt::vformat(mKey, mArgs);
82 } catch (...) {
83 return mKey;
84 }
85
86 std::string const& key() const noexcept { return mKey; }
87 fmt::dynamic_format_arg_store<fmt::format_context> const& args() const noexcept { return mArgs; }
88};
89
90} // namespace ll::i18n
91
92#ifdef LL_I18N_COLLECT_STRINGS
93#include "ll/api/reflection/TypeName.h"
94
95#ifndef LL_I18N_STRING_LITERAL_TYPE
96#define LL_I18N_STRING_LITERAL_TYPE ::ll::FixedStrWithLoc
97#endif
98
99namespace ll::i18n::detail {
100template <LL_I18N_STRING_LITERAL_TYPE str>
101struct TrStrOut;
102#ifndef LL_I18N_COLLECT_STRINGS_CUSTOM
103template <LL_I18N_STRING_LITERAL_TYPE str>
104struct TrStrOut {
105 static inline int _ = [] {
106 fmt::print("\"{0}\": \"{0}\", // at {1}\n", str.sv(), str.loc().toString());
107 return 0;
108 }();
109};
110#endif
111} // namespace ll::i18n::detail
112#else
113#ifndef LL_I18N_STRING_LITERAL_TYPE
114#define LL_I18N_STRING_LITERAL_TYPE ::ll::FixedString
115#endif
116#endif
117
118namespace ll {
119template <LL_I18N_STRING_LITERAL_TYPE Fmt, typename... Args>
120[[nodiscard]] inline Unexpected makeI18nStringError(Args&&... args) noexcept {
121#ifdef LL_I18N_COLLECT_STRINGS
122 static i18n::detail::TrStrOut<Fmt> e{};
123#endif
124 return makeError<i18n::I18nStringError>(fmt::format_string<Args...>(Fmt.sv()), std::forward<Args>(args)...);
125}
126} // namespace ll
127
128namespace ll::inline literals::inline i18n_literals {
129template <LL_I18N_STRING_LITERAL_TYPE Fmt>
130[[nodiscard]] constexpr auto operator""_tr() {
131#ifdef LL_I18N_COLLECT_STRINGS
132 static i18n::detail::TrStrOut<Fmt> e{};
133#endif
134 return [=]<class... Args>(Args&&... args) {
135 [[maybe_unused]] static constexpr auto checker = fmt::format_string<Args...>(Fmt.sv());
136 return fmt::vformat(i18n::getInstance().get(Fmt.sv(), {}), fmt::make_format_args(args...));
137 };
138}
139template <LL_I18N_STRING_LITERAL_TYPE Fmt>
140[[nodiscard]] constexpr auto operator""_trl() {
141#ifdef LL_I18N_COLLECT_STRINGS
142 static i18n::detail::TrStrOut<Fmt> e{};
143#endif
144 return [=]<class... Args>(std::string_view localeCode, Args&&... args) {
145 [[maybe_unused]] static constexpr auto checker = fmt::format_string<Args...>(Fmt.sv());
146 return fmt::vformat(i18n::getInstance().get(Fmt.sv(), localeCode), fmt::make_format_args(args...));
147 };
148}
149} // namespace ll::inline literals::inline i18n_literals
Definition I18n.h:26