17 if (!
node.is_object())
22 for (
const auto& [
key, value] :
node.get_object())
25 if (value.is_object())
29 else if (value.is_string())
45 {
"cmd.noPermission",
"You do not have permission to use this command."},
46 {
"cmd.tooManyArgs",
"Too many arguments. Usage: {usage}"},
47 {
"cmd.usage",
"Usage: {usage}"},
48 {
"cmd.usage.target",
"target"},
49 {
"cmd.usage.targets",
"targets"},
50 {
"cmd.usage.duration",
"duration"},
51 {
"cmd.usage.steamId",
"steamId"},
52 {
"cmd.usage.playerOrSteamId",
"target|steamId"},
53 {
"cmd.usage.int",
"number"},
54 {
"cmd.usage.u64",
"id"},
55 {
"cmd.usage.word",
"value"},
56 {
"cmd.usage.rest",
"reason"},
57 {
"cmd.badDuration",
"Invalid duration. Use minutes (e.g. 30), 30s/5m/2h/7d, or 'perm'."},
58 {
"cmd.badSteamId",
"'{token}' is not a valid SteamID64."},
59 {
"cmd.badNumber",
"'{token}' is not a valid number."},
60 {
"target.noMatch",
"No player matches '{token}'."},
61 {
"target.immune",
"'{token}' is immune to that."},
62 {
"target.ambiguous",
"'{token}' matches {count} players - be more specific."},
63 {
"target.dead",
"'{token}' is not alive."},
64 {
"target.bot",
"'{token}' is a bot."},
65 {
"menu.stepFailed",
"That menu could not be opened."},
68 {
"menu.confirm",
"Confirm"},
69 {
"menu.cancel",
"Cancel"},
70 {
"language.en",
"English"},
71 {
"language.ru",
"Russian"},
80 _translations.clear();
81 namespace fs = std::filesystem;
94 if (
entry.path().extension() !=
".json")
125 if (
auto en = _translations.find(
"en");
en != _translations.end() && _translations.size() > 1)
127 for (
const auto& [
code,
keys] : _translations)
135 for (
const auto& [
key, value] :
en->second)
165 std::vector<std::string>
langs;
166 langs.reserve(_translations.size());
167 for (
const auto& [
code,
_] : _translations)
185std::optional<std::string_view> Translations::LookupIn(std::string_view
lang, std::string_view
key)
const
188 if (
langIt != _translations.end())
193 return keyIt->second;
204std::optional<std::string_view> Translations::Resolve(std::string_view
key,
int slot)
const
207 const std::string_view
lang =
picked.empty() ? std::string_view(_activeLang) :
picked;
209 for (
const std::string_view
candidate : {
lang, std::string_view(_activeLang), std::string_view(
"en")})
static Result< glz::generic > ParseDocument(std::string_view text)
Parse free-form JSON whose shape is not known at compile time.
virtual std::string_view Language(int slot) const =0
virtual void SetLanguage(int slot, std::string_view lang)=0
static std::string SubstituteTokens(std::string text, const Tokens &tokens)
void SetLanguage(std::string_view lang)
std::string_view PlayerLanguage(int slot) const
Translations(PlayerLanguages &languages)
std::string Get(std::string_view key) const
std::vector< std::string > GetAvailableLanguages() const
bool Load(std::string_view dirPath)
void SetPlayerLanguage(int slot, std::string_view lang)
const std::string & GetLanguage() const
std::string GetOr(std::string_view key, int slot, std::string_view fallback) const
void Info(std::format_string< Args... > fmt, Args &&... args)
void Warn(std::format_string< Args... > fmt, Args &&... args)
static void FlattenInto(const glz::generic &node, const std::string &prefix, StringMap< std::string > &out)
std::filesystem::path ResolvePath(std::string_view relativePath)
a relative path against the base directory. If the path is already absolute, returns it as-is.
static std::string ReadFile(const std::filesystem::path &path)
static const StringMap< std::string > & KitDefaults()
std::unordered_map< std::string, Value, StringHash, std::equal_to<> > StringMap
Result< std::string > ReadAllText(std::string_view path)
Read path (resolved via ResolvePath) into a string.
std::map< std::string, std::string > Tokens